【问题标题】:Extracting a string between other two strings in R在R中的其他两个字符串之间提取一个字符串
【发布时间】:2021-12-21 05:16:36
【问题描述】:

我试图找到一种简单的方法来提取出现在两个已知子字符串之间的未知子字符串(可能是任何东西)。比如我有一个字符串:

a<-" anything goes here, STR1 GET_ME STR2, anything goes here"

我需要提取字符串GET_ME,它位于STR1 和STR2 之间(没有空格)。

我正在尝试str_extract(a, "STR1 (.+) STR2"),但我得到了整场比赛

[1] "STR1 GET_ME STR2"

我当然可以剥离已知字符串,以隔离我需要的子字符串,但我认为应该有一种更简洁的方法来使用正确的正则表达式来做到这一点。

【问题讨论】:

标签: regex r stringr


【解决方案1】:

您可以将str_matchSTR1 (.*?) STR2 一起使用(注意空格是“有意义的”,如果您只想匹配STR1STR2 之间的任何内容,请使用STR1(.*?)STR2,或使用STR1\\s*(.*?)\\s*STR2 修剪您需要的值)。如果出现多次,请使用str_match_all

此外,如果您需要匹配跨越换行符/换行符的字符串,请在模式开头添加 (?s)(?s)STR1(.*?)STR2 / (?s)STR1\\s*(.*?)\\s*STR2

library(stringr)
a <- " anything goes here, STR1 GET_ME STR2, anything goes here"
res <- str_match(a, "STR1\\s*(.*?)\\s*STR2")
res[,2]
[1] "GET_ME"

使用base R regexec 的另一种方式(获取第一个匹配项):

test <- " anything goes here, STR1 GET_ME STR2, anything goes here STR1 GET_ME2 STR2"
pattern <- "STR1\\s*(.*?)\\s*STR2"
result <- regmatches(test, regexec(pattern, test))
result[[1]][2]
[1] "GET_ME"

【讨论】:

  • 有效!问号的目的是什么?没有它似乎也可以工作。
  • ?lazy(非贪婪)量词的一部分。它匹配尽可能少的字符,而* 将匹配尽可能多的字符。所以,STR1 .*? STR2 正则表达式匹配STR1 xx STR2,而STR1 .* STR2 将匹配STR1 xx STR2 zzz STR2。如果您希望输入中有多个匹配项,则此处必须使用惰性量词。另外,仅供参考:如果STR1STR2 之间的字符串部分可能包含换行符,则需要在模式前面加上(?s)"(?s)STR1 (.*?) STR2"
  • @Wiktor:你能解释一下为什么str_match 的输出在矩阵中吗?这似乎很不方便,尤其是当大多数人想要的唯一输出是[,2]
  • @Nettle 我不同意,因为如果有人只想要[,2],他们应该只使用regmatches(a, regexpr("STR1\\s*\\K.*?(?=\\s*STR2)", a, perl=TRUE))。使用stringr,也可以使用str_extract_all(a, "(?s)(?&lt;=STR1\\s{0,1000}).*?(?=\\s*STR2)") 之类的模式(尽管出于某种原因,该空格仍包含在匹配中,而且相当hacky)。 str_match 是您需要返回所有匹配和捕获的救星。此外,可以与str_match 一起使用的模式效率更高。
  • @Wiktor: regmatches/regexpr 组合阻塞了一个在 stringr 中很好的表达式......所以你的表达式 str_extract_all(a, "(?s)(?&lt;=STR1\\s{0,1000}).*?(?=\\s*STR2)") 不能用作 regmatches(a,regexpr("(?s)(?&lt;=STR1\\s{0,1000}).*?(?=\\s*STR2)", a, perl = TRUE) ) 为什么会这样?
【解决方案2】:

这是另一种使用基础 R 的方法

a<-" anything goes here, STR1 GET_ME STR2, anything goes here"

gsub(".*STR1 (.+) STR2.*", "\\1", a)

输出:

[1] "GET_ME"

【讨论】:

    【解决方案3】:

    另一种选择是使用qdapRegex::ex_between 提取左右边界之间的字符串

    qdapRegex::ex_between(a, "STR1", "STR2")[[1]]
    #[1] "GET_ME"
    

    它也适用于多次出现

    a <- "anything STR1 GET_ME STR2, anything goes here, STR1 again get me STR2"
    
    qdapRegex::ex_between(a, "STR1", "STR2")[[1]]
    #[1] "GET_ME"       "again get me"
    

    或多个左右边界

    a <- "anything STR1 GET_ME STR2, anything goes here, STR4 again get me STR5"
    qdapRegex::ex_between(a, c("STR1", "STR4"), c("STR2", "STR5"))[[1]]
    #[1] "GET_ME"       "again get me"
    

    第一次捕获在“STR1”和“STR2”之间,而第二次在“STR4”和“STR5”之间。

    【讨论】:

      【解决方案4】:

      我们可以使用{unglue},在这种情况下我们根本不需要正则表达式:

      library(unglue)
      unglue::unglue_vec(
        " anything goes here, STR1 GET_ME STR2, anything goes here", 
        "{}STR1 {x} STR2{}")
      #> [1] "GET_ME"
      

      {} 匹配任何内容而不保留它,{x} 捕获其匹配项(可以使用除x 之外的任何变量。语法"{}STR1 {x} STR2{}" 的缩写为:"{=.*?}STR1 {x=.*?} STR2{=.*?}"

      如果你也想提取侧面,你可以这样做:

      unglue::unglue_data(
        " anything goes here, STR1 GET_ME STR2, anything goes here", 
        "{left}, STR1 {x} STR2, {right}")
      #>                  left      x              right
      #> 1  anything goes here GET_ME anything goes here
      

      【讨论】:

      • 如果我们想使用任何变量来代替 STR1 和 STR2,我们该怎么做。假设我将 STR1 分配给 a 并将 STR2 分配给 b,现在我们如何使用正则表达式来提取 a 和 b 之间的字符串
      • 您可以使用sprintf("{left}, %s {x} %s, {right}", a, b)paste0("{left}, ", a, " {x} ", b, ", {right}") 代替"{left}, STR1 {x} STR2, {right}"
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-26
      • 1970-01-01
      • 1970-01-01
      • 2019-06-25
      • 1970-01-01
      相关资源
      最近更新 更多