在 gsub 期间保留换行符并在字符串中有选择地缩进

Keep newline character and selectively indent in string during gsub

原标题:在gsub期间在字符串中保留换行符

有一个 ,我尝试将 JSON 转换为降价无序列表。快完成了,但是有一种模式我无法处理。如果字符串中包含 space、换行符、space 序列,则它将被视为列表项连字符。如果我尝试使用一些对换行符的引用来避免这种情况,那么一切都不会像我预期的那样起作用。

输入 JSON: https://gist.github.com/hermanp/381eaf9f2bf5f2b9cdf22f5295e73eb5
首选输出(两个space缩进)markdown:

- Info
  - Python
    - The Ultimate Python Beginner's Handbook
    - Python Like You Mean It
    - Automate the Boring Stuff with Python
    - Data science Python notebooks
  - Frontend
    - CodePen
    - JavaScript - Wikipedia
    - CSS-Tricks
    - Butterick’s Practical Typography
    - Front-end Developer Handbook 2019
    - Using Ethics In Web Design
    - Client-Side Web Development
  - Stack Overflow
  - HUP
  - Hope in Source

为了生成降价,我使用了以下两个脚本:
generate_md()

library(jsonlite)

generate_md <- function (jsonfile) {
  bmarks_json_lite <- fromJSON(txt = jsonfile)
  level1 <- bmarks_json_lite$children$children[[2]]
  markdown_result <- recursive_func(level = level1)
  return(markdown_result)
}

recursive_func()

recursive_func <- function (level) {
  md_result <- character()
  
  for (i in seq_len(nrow(level))) {
    if (level[i, "type"] == "text/x-moz-place"){
      md_title <- paste0("- ", level[i, "title"], "\n")
    } else if (level[i, "type"] == "text/x-moz-place-container") {
      md_title <- paste0("- ", level[i, "title"], "\n")
      md_recurs <- recursive_func(level = level[i, "children"][[1]])
      
      # >>>>> This is the problematic part. <<<<<
      md_recurs <- gsub("-(?= )", "  -", md_recurs, perl = T)
      md_title <- paste0(md_title, md_recurs)
    }
    
    md_result <- paste0(md_result, md_title)
  }
  
  return(md_result)
}

通过这些函数,我可以实现以下目标(注意 JavaScript 维基百科条目中不必要的 spaces)。我想得到 - JavaScript - Wikipedia 而不是 - JavaScript - Wikipedia。我希望这个例子代表带有连字符和缩进的不同场景,但这仍然只是我书签的一小部分。我想举一个最小的例子。

cat(generate_md(paste0("https://gist.githubusercontent.com/hermanp/",
                       "381eaf9f2bf5f2b9cdf22f5295e73eb5/raw/",
                       "76b74b2c3b5e34c2410e99a3f1b6ef06977b2ec7/",
                       "bookmarks-example-hyphen.json")))
# Output
- Info
  - Python
    - The Ultimate Python Beginner's Handbook
    - Python Like You Mean It
    - Automate the Boring Stuff with Python
    - Data science Python notebooks
  - Frontend
    - CodePen
    - JavaScript     - Wikipedia
    - CSS-Tricks
    - Butterick’s Practical Typography
    - Front-end Developer Handbook 2019
    - Using Ethics In Web Design
    - Client-Side Web Development
  - Stack Overflow
  - HUP
  - Hope in Source

我修改了 recursive_func 中的 gsub 函数部分,如下所示,没有期望的输出:

md_recurs <- gsub("-(?= )", "  -", md_recurs, perl = T)  # Original
md_recurs <- gsub("(\n)?-(?= )", "  -", md_recurs, perl = T)  # No newlines
md_recurs <- gsub("(-)(?= )(?<=\n)?", "  -", md_recurs, perl = T)  # Same as Original

在 Google 上搜索 regex newline before char gsub site:whosebug.com,我找不到这个问题的答案或提示。我也玩过regex101.com,但找不到正确的路径。

您可以使用

gsub("\w\h+-\h(*SKIP)(*F)|-(?=\h)", "  -", x, perl=TRUE)

regex demo。详情:

  • \w - 一个字 char
  • \h+ - 一个或多个水平空格
  • - - 一个 -char
  • \h - 水平空格
  • (*SKIP)(*F) - 省略到目前为止匹配的文本,匹配失败并从失败的位置开始搜索
  • | - 或
  • - - 一个 - 字符
  • (?=\h) - 紧跟水平空格。

在我仔细考虑了问题和字符串的结构并阅读了 lookbehind 之后,我终于想出了解决方案。

md_recurs行需要修改为:

md_recurs <- gsub("(?<!(\w ))-(?= )", "  -", md_recurs, perl = T)

这意味着 gsub() pattern 参数必须修改为:

(?<!(\w ))-(?= )

这意味着:

  • 替换一个连字符 -(改为两个 space 和一个连字符 -
  • 如果前面没有一个单词串和一个space(?<!(\w ))
  • 如果后面没有space (?= ).