【问题标题】:How can I split a string and ignore the delimiter if it's "quoted"如果它被“引用”,我如何拆分字符串并忽略分隔符
【发布时间】:2018-09-21 08:56:53
【问题描述】:

假设我有以下字符串:

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

我想使用, 作为分隔符来拆分它,然后提取“引用的子字符串”,所以我得到了 2 个向量,如下所示:

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

我在广义上使用术语“引用”,带有任意字符串,这里是/**/,它们可以保护子字符串不被拆分。

我发现了一个基于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="")

但它非常丑陋和hackish,有什么更简单的方法?我想在这种情况下必须有一个regex 来提供给strsplit

this question相关

【问题讨论】:

    标签: r regex strsplit


    【解决方案1】:

    您可以将它包装在一个函数中并使用(没有很好的文档记录的)(*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,].

    【讨论】:

      【解决方案2】:

      你来了

      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))
      

      【讨论】:

      • 不行,你忘了params_df的第二个元素,是一个空字符串。
      • @Moody_Mudskipper 为什么会有一个空字符串?
      • 因为var2 旁边没有任何注释文字
      • @Moody_Mudskipper 我已经修改了代码以先拆分字符串
      【解决方案3】:

      你可以使用

      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+ 个非星号字符 ([^*]*) 后跟 1+ 个星号 (\*+)
      • / - 关闭/
      • )? - 捕获组 2 结束,重复 1 或 0 次(这意味着它是可选的)。

      请参阅regex demo

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-16
        • 1970-01-01
        • 2010-09-05
        相关资源
        最近更新 更多