如果它是 "quoted",如何拆分字符串并忽略分隔符

How can I split a string and ignore the delimiter if it's "quoted"

假设我有以下字符串:

params <- "var1 /* first, variable */, var2, var3 /* third, variable */"

我想使用 , 作为分隔符拆分它,然后提取 "quoted substrings",所以我得到 2 个向量如下:

params_clean <- c("var1","var2","var3")
params_def   <- c("first, variable","","third, variable") # note the empty string as a second element.

我在广义上使用术语"quoted",使用任意字符串,这里是/**/,它们保护子字符串不被拆分。

我找到了一个基于 read.table 的解决方法,事实上它允许引用元素:

library(magrittr)
params %>%
  gsub("/\*","_temp_sep_ '",.) %>%
  gsub("\*/","'",.) %>%
  read.table(text=.,strin=F,sep=",") %>%
  unlist %>%
  unname %>%
  strsplit("_temp_sep_") %>%
  lapply(trimws) %>%
  lapply(`length<-`,2) %>%
  do.call(rbind,.) %>%
  inset(is.na(.),value="")

但它非常丑陋和骇人听闻,有什么更简单的方法吗?对于这种情况,我认为必须有一个 regex 可以提供给 strsplit

相关

给你

library(stringr)
params <- "var1 /* first, variable */, var2, var3 /* third, variable */"
# Split by , which are not enclosed in your /*...*/ 
params_split <- str_split(params, ",(?=[^(/[*])]*(/[*]))")[[1]]
# Extract matches of /*...*/, only taking the contents
params_def <- str_match(params_split, "/[*] *(.*?) *[*]/")[,2]
params_def[is.na(params_def)] <- ""
# Remove traces of /*...*/
params_clean <- trimws(gsub("/[*] *(.*?) *[*]/", "", params_split))

您可以将它包装在一个函数中并使用(没有很好记录的)(*SKIP)(*FAIL) 机制以简单的方式 R:

getparams <- function(params) {
  tmp <- unlist(strsplit(params, "/\*.*?\*/(*SKIP)(*FAIL)|,", perl = TRUE))

  params_clean <- vector(length = length(tmp))
  params_def <- vector(length = length(tmp))

  for (i in seq_along(tmp)) {
    # get params_def if available
    match <- regmatches(tmp[i], regexec("/\*(.*?)\*/", tmp[i]))
    params_def[i] <- ifelse(identical(match[[1]], character(0)), "", trimws(match[[1]][2]))

    # params_clean
    params_clean[i] <- trimws(gsub("/(.*)\*.*?\*/", "\1", tmp[i]))
  }

  return(list(params_clean = params_clean, params_def = params_def))
}

params <- "var1 /* first, variable */, var2, var3 /* third, variable */"
getparams(params)

这将使用 (*SKIP)(*FAIL)(请参阅 a demo on regex101.com)拆分初始字符串,然后分析各个部分。


这会产生一个列表:

$params_clean
[1] "var1" "var2" "var3"

$params_def
[1] "first, variable" ""                "third, variable"


或者,更短 sapply:

getparams <- function(params) {
  tmp <- unlist(strsplit(params, "/\*.*?\*/(*SKIP)(*FAIL)|,", perl = TRUE))
  (p <- sapply(tmp, function(x) {
    match <- regmatches(x, regexec("/\*(.*?)\*/", x))
    def <- ifelse(identical(match[[1]], character(0)), "", trimws(match[[1]][2]))
    clean <- trimws(gsub("/(.*)\*.*?\*/", "\1", x))
    c(clean, def)
  }, USE.NAMES = F))
}

这将产生一个矩阵:

     [,1]              [,2]   [,3]             
[1,] "var1"            "var2" "var3"           
[2,] "first, variable" ""     "third, variable"

对于后者,您可以获得变量名称,例如result[1,].

您可以使用

library(stringr)
cmnt_rx <- "(\w+)\s*(/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)?"
res <- str_match_all(params, cmnt_rx)
params_clean <- res[[1]][,2]
params_clean
## => [1] "var1" "var2" "var3"
params_def <- gsub("^/[*]\s*|\s*[*]/$", "", res[[1]][,3])
params_def[is.na(params_def)] <- ""
params_def
## => [1] "first, variable" ""                "third, variable"

正则表达式的主要细节(实际上是(\w+)\s*)(COMMENTS_REGEX)?):

  • (\w+) - 捕获第 1 组:一个或多个单词字符
  • \s* - 0+ 个空白字符
  • ( - 捕获组 2 开始
  • /\* - 匹配注释开始/*
  • [^*]*\*+ - 匹配 * 以外的 0+ 个字符,后跟 1+ 个文字 *
  • (?:[^/*][^*]*\*+)* - 0+ 个序列:
    • [^/*][^*]*\*+ - 不是 /*(与 [^/*] 匹配)后跟 0+ non-asterisk 个字符 ([^*]*)后跟 1+ 个星号 (\*+)
  • / - 收盘 /
  • )? - 捕获第2组结束,重复1次或0次(表示可选)。

参见regex demo

gsub 中的 "^/[*]\s*|\s*[*]/$" 模式删除了带有相邻空格的 /**/

params_def[is.na(params_def)] <- "" 部分将 NA 替换为空字符串。