【问题标题】:r grep by regex - finding a string that contains a sub string exactly one oncer grep by regex - 查找包含恰好一盎司子字符串的字符串
【发布时间】:2016-07-12 09:57:42
【问题描述】:

我在 Ubuntu 中使用 R,并尝试查看文件列表,其中一些我需要,一些我不需要,

我试图通过在其中找到一个子字符串来获得我需要的那个,它需要只出现一次,

我正在使用函数 grep,我在这里找到了 grep function in r

并使用我在这里找到的正则表达式规则regex rules

当举个简单的例子时

a <- c("a","aa") 
grep("a{1}", a) 

我希望只得到一次恰好包含“a”的字符串,而不是我得到它们。

当我使用 2 而不是 1 时,我确实得到了一个字符串(包含“aa”的那个)的想要结果

我不能使用 $ 因为这不是我需要的单词的结尾,例如我需要使用这两个单词“germ-pass.tab”、“germ-pass_germ-pass.tab”并且只返回第一个包含“germ-pass”的第一个,并且只返回一次

我不能使用^a,因为我不需要诸如“aca”之类的词

谢谢。

【问题讨论】:

  • grep 在您的string 中查找模式,您需要添加到"a" 后面不是:"a[^a]" 的模式中
  • 我发现我们可以使用 {} 来查找确切的出现次数,我正在尝试使用一个简单的正则表达式,对于一些比找到“a”更复杂的东西......试图找出我做错了什么{}
  • 问题是,在“aa”中,有模式“a”,恰好出现一次。它恰好后面跟着另一个“a”......
  • 你没有做错什么。您的两个示例都包含一次a。第一个与a{2} 不匹配。
  • stringi::stri_count()

标签: regex r


【解决方案1】:

正如我在 cmets 中所说,grep 在您的字符串中寻找一个模式,并且在“aa”中确实存在“a”(或“a{1}”,这与 grep 相同)。您需要添加到“a”后面不是 a 的模式:"a[^a]":

grep("a[^a]", c("aa", "ab"), value=TRUE)
#[1] "ab"

编辑

考虑到您的具体问题,您似乎可以尝试“相反”:使用模式的“捕获”过滤掉包含多次出现该模式的字符串:

!grepl("(ab).+\\1", c("ab.t", "ab-ab.t"))
#[1]  TRUE FALSE

!grepl("(ab).*\\1", c("ab", "ab-ab","ab-cc-ab", "abab"))
#[1]  TRUE FALSE FALSE FALSE

方括号允许捕获模式(此处为ab,但它可以是任何正则表达式),.* 用于“任何”零​​次或多次,\\1 要求重复捕获的模式

【讨论】:

  • 不,这对我不好,可能有“aca”,这是我需要过滤掉的词。我不需要这个词@Cath
  • @captainshai 看到我的编辑,它可能更好地满足您的需求
  • !grepl("(ab).\\1", c("ab", "ab-ab","ab-cc-ab")) [1] TRUE FALSE TRUE 这是也有问题...
  • @captainshai 然后您需要将. 更改为与您的文件名对应的正则表达式,这里:!grepl("(ab).+\\1", c("ab", "ab-ab","ab-cc-ab")),我进行了相应的编辑
  • 无论如何都可以使用 {1} 来查找仅包含一次子字符串的字符串 - 否。更多:{1} 是多余的,应该从任何模式中删除.
【解决方案2】:

检测带有a 但不是aa 的字符串

您可以使用以下 TRE 正则表达式:

^[^a]*a[^a]*$

它匹配字符串的开头 (^)、除a 之外的 0+ 个字符 ([^a]*)、a、再次匹配 0+ 非'a's 和字符串结尾 (@987654332 @)。看到这个IDEONE demo

a <- c("aca","cac","a", "abab", "ab-ab", "ab-cc-ab")
grep("^[^a]*a[^a]*$", a, value=TRUE)
## => [1] "cac" "a"

查找包含a 但不包含aa 的整个单词

如果您需要匹配单词,在任何位置只有一个a,而不是两个或更多as。

使用这个 PCRE 正则表达式:

\b(?!\w*a\w*a)\w*a\w*\b

this regex demo

解释

  • \b - 字边界
  • (?!\w*a\w*a) - 如果在单词边界之后有 0+ 个单词字符、a、0+ 个单词字符和 a,则匹配失败
  • \w* - 0+ 个单词字符
  • a - 一个a
  • \w* - 0+ 个单词字符
  • \b - 词尾边界。

注意:由于\w 匹配字母、数字和下划线,您可能需要将其更改为\p{L}[^\W\d_](仅匹配字母)。

this demo:

a <- c("aca","cac","a")
grep("\\b(?!\\w*a\\w*a)\\w*a\\w*\\b", a, perl=TRUE, value=TRUE)
## => [1] "cac" "a"  

【讨论】:

  • 对不起,不是一个独立的词,而是一个词的一部分......例如,从词 c("aca","cac","a") 我只需要最后一个两个
  • stribizew 我不能使用 \\b,这不保证是单词的开头
  • 然后删除它。您能否分享一个我的正则表达式无法获取您需要的内容的案例?
  • 这是否意味着您需要匹配整个字符串?喜欢^[^a]*a[^a]*$
【解决方案3】:

看起来您正在寻找带有一个 a 的字符串,而不管字符串中的哪个位置。虽然stringi 可以完成任务,但基本解决方案是:

s <- c("a", "aa", "aca", "", "b", "ba", "ab")

m <- gregexpr("a", s)
s[lengths(regmatches(s, m)) == 1]

[1] "a"  "ba" "ab"

或者,一种 regex-lite 方法:

s[vapply(strsplit(s, ""), function(x) sum(x == "a") == 1, logical(1))]
[1] "a"  "ba" "ab"

【讨论】:

    【解决方案4】:

    我们可以使用stringi::stri_count:

    library(stringi)
    library(purrr)
    
    # simulate some data
    set.seed(1492)
    (map_chr(1:10, function(i) {
      paste0(sample(letters, sample(10:30), replace=TRUE), collapse="")
    }) -> strings)
    
    ## [1] "jdpcypoizdzvfzs"               "gyvcljnfmrzmdmkufq"           
    ## [3] "xqwrmnklbixnccwyaiadrsxn"      "bwbenawcwvdevmjfvs"           
    ## [5] "ytzwnpkuromfbklfsdnbwwnlrw"    "wclxpzftqgwxyetpsuslgohcdenuj"
    ## [7] "czkhanefss"                    "mxsrqrackxvimcxqcqsditrou"    
    ## [9] "ysqshvzjjmwes"                 "yzawyoqxqxiasensorlenafcbk" 
    
    # How many "w"s in each string?
    stri_count_regex(strings, "w{1}")
    
    ## [1] 0 0 2 3 4 2 0 0 1 1
    

    【讨论】:

      【解决方案5】:

      我们可以尝试使用^$ 来确保字符串中只有一个'a'

      grep("^a$", a)
      #[1] 1
      

      不清楚 OP 想要什么。

      【讨论】:

      • 我不能使用 $,这不是我正在寻找的单词的结尾......但仍然为答案投票......
      • @captainshai 请使用代表问题的示例
      • 我添加了相关示例,请解释您所说的“OP”是什么意思
      • @captainshai 我的意思是原始海报
      • 谢谢。答案很好,但我担心它不适合我这里的需要......
      【解决方案6】:

      base 中,当您使用 gsub 删除子字符串并测试剩余字符串长度是否为等于搜索到的子字符串:

      s <- c("a", "aa", "aca", "", "b", "ba", "ab", "cac", "abab", "ab-ab", NA)
      ss  <- "a" #Substring to find exactly once
      
      nchar(s) - nchar(gsub(ss, "", s)) == nchar(ss)
      #[1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE    NA
      

      或者你计算gregexpr的点击次数

      sapply(gregexpr(ss, s), function(x) sum(x>0)) == 1
      # [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE    NA
      

      或者正如@sebastian-c 已经提到的那样

      lengths(regmatches(s, gregexpr(ss, s))) == 1
      # [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
      

      或者用两个grepl 一个询问子字符串是否存在一次,如果它存在两次:

      !grepl("(.*a){2}", s) & grepl("a", s)
      # [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
      

      或在一个正则表达式中解释的相同,其中(?!(.*a){2}) 是非消耗性(零宽度)负前瞻

      grepl("^(?!(.*a){2}).*a.*$", s, perl=TRUE)
      # [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
      

      或者更一般的,如果你想改变它来准确地找到子字符串 n 次

      !grepl("(.*a){2}", s) & grepl("(.*a){1}", s)
      # [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
      
      grepl("^(?!(.*a){2})(.*a){1}.*$", s, perl=TRUE)
      # [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
      

      如果您只寻找一个字符,您可以使用解决方案表单@wiktor-stribiżew

      grepl("^[^a]*a[^a]*$", s)
      # [1]  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE
      

      【讨论】:

        猜你喜欢
        • 2021-12-05
        • 1970-01-01
        • 2018-12-20
        • 2022-08-19
        • 1970-01-01
        • 2023-03-14
        • 2020-04-07
        • 1970-01-01
        • 2014-03-23
        相关资源
        最近更新 更多