【问题标题】:Regex to remove non-adjacent duplicates in R正则表达式删除 R 中不相邻的重复项
【发布时间】:2020-02-22 05:00:30
【问题描述】:

努力想出一个删除字符串中重复字符的正则表达式(即只保留第一次出现的字符)

特别是我想在第一次出现后删除所有冒号

s <- c("note: conducted by j:m",
       "location: made :n :apan",
       "date: 2010, ma: 3rd")

成为

note: conducted by jm
location: made in apan
date: 2010, ma 3rd

我能做到的最好是

library(stringr)
str_remove(string = s, pattern = "(?<=:)(.*?):(.*)")
# [1] "note:"     "location:"     "date:"

编辑:

我能得到的第二好的是"(:)(?=.*\1)" on regex.com - 它可以识别除最后一个 : 之外的所有内容(这与我想要的相反)但在 R 中不起作用,不知道为什么?

str_remove(string = s, pattern = "(:)(?=.*\1)")
# [1] "note: conducted by j:m"  "location: made :n :apan" "date: 2010, ma: 3rd" 

【问题讨论】:

    标签: r regex stringr


    【解决方案1】:

    试试this 模式:

    (?:^[^:]*:|\G(?!^))[^:]*\K:
    

    现在,我不知道这是否适用于 Ruby,因为老实说,我对此几乎一无所知。

    我确实让 this 样本在单个字符串上工作,让我认为它应该工作 =)

    str = "location: made : in japan: or what:"
    str.gsub(/(?:^[^:]*:|\G(?!^))[^:]*\K:/, '')
    

    【讨论】:

    • 完美...使用gsub(x = s, pattern = "(?:^[^:]*:|\\G(?!^))[^:]*\\K:", replacement = "", perl = TRUE)工作
    【解决方案2】:

    在这里分两步尝试使用sub

    first <- sub("^(.*?:).*", "\\1", s)
    second <- sub("^.*?:", "", s)
    second <- sub(":", "", second, fixed=TRUE)
    out <- paste0(first, second)
    out
    
    [1] "note: conducted by jm"  "location: made in apan" "date: 2010, ma 3rd"
    

    数据:

    s <- c("note: conducted by j:m",
           "location: made in :apan",
           "date: 2010, ma: 3rd")
    

    这里的方法是在一个变量中捕获每个字符串,直到并包括第一个冒号,然后在第二个变量中捕获每个字符串的其余部分。然后,我们只从第二个变量中删除冒号,最后将两部分粘贴在一起以生成预期的输出。

    请注意,更简洁的方法是使用 substr_replace_all 与回调函数,但语法并不那么简单。

    【讨论】:

    • 寻找一种更简洁的方法来做到这一点,即直接的正则表达式......在某些行上我有两个以上 : 并且我正在运行数万行,所以通过创建中间对象我担心速度 - 也许是不必要的?
    • 正如我在回答中提到的,您可能会找到一些方法来使用带有回调函数的单线进行正则表达式替换。 但是,即使这样也不一定比我的答案中的解决方案更有效。
    【解决方案3】:

    不幸的是,“Look-Behind 模式匹配必须有一个有界的最大长度。

    所以,Tim's approach 可能是要走的路

    1. 将每个字符串分成两部分,
    2. 从第二部分中删除所有:,然后
    3. 重新连接第一部分和修改后的第二部分

    这是一个单行实现:

    library(stringr)
    sapply(str_split(s, "(?<=:)", n = 2L), function(x) paste0(x[1L], str_remove_all(x[-1L], ":")))
    

    返回

    [1] "note: conducted by jm"    "location: made n apan"    "date: 2010, ma 3rd"      
    [4] "no double colon"          "test1:"                   "test2: colon1 colon2 end"
    

    对于增强的样本字符向量(包括一些边缘情况):

    s <- c("note: conducted by j:m",
           "location: made :n :apan",
           "date: 2010, ma: 3rd",
           "no double colon",
           "test1::::",
           "test2: colon1: colon2: end")
    

    【讨论】:

      【解决方案4】:

      从所述问题的外观来看,正则表达式似乎太复杂了。非正则表达式方式不是更简单吗?喜欢这个:

      for s in ss:
          it = re.subn( ":", "#", s, count=1 ) #Replace the first ":" with "#" to distinguish it from others
          print( it[0].replace( ":", "" ).replace( "#", ":" ) ) #Remove remaining ":"s and bring back the original
      

      当然,如果您出于其他原因更喜欢基于正则表达式的解决方案,则不会这样做。 (另外,给出的解决方案是 Python 而不是 R,你可能已经注意到了。)

      【讨论】:

      • @TimBiegeleisen 好点。如果输入数据的性质已知,我们可以相应地选择一个或多个字符。例如,如果我知道#$#$^&amp;^#$ 永远不会出现在输入字符串中,我可以选择它而不是#
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-12
      • 1970-01-01
      • 2013-01-08
      相关资源
      最近更新 更多