【问题标题】:Test if characters are in a string测试字符是否在字符串中
【发布时间】:2012-04-25 02:19:15
【问题描述】:

我正在尝试确定一个字符串是否是另一个字符串的子集。例如:

chars <- "test"
value <- "es"

如果“value”作为字符串“chars”的一部分出现,我想返回 TRUE。在以下情况下,我想返回 false:

chars <- "test"
value <- "et"

【问题讨论】:

  • 接受的答案是错误的,您需要添加fixed=TRUE,否则您将其视为正则表达式而不是字符串。请参阅我 2016 年 10 月的回答。
  • @JoshuaCheek 除非您的模式中有特殊字符,否则正则表达式将返回与固定相同的结果。
  • 当然,但只有在传递文字时才能知道。否则,您将不知道模式中的字符是什么,因此您要么使用fixed=TRUE,要么有一个错误会悄悄地巧妙地弄乱您的数据。

标签: r regex string r-faq


【解决方案1】:

使用grepl 函数

grepl( needle, haystack, fixed = TRUE)

像这样:

grepl(value, chars, fixed = TRUE)
# TRUE

使用?grepl 了解更多信息。

【讨论】:

  • 对于这种简单的情况,添加 fixed=TRUE 可能会提高性能(假设您将进行大量此类计算)。
  • @Josh O'brien,该帖子比较了在单个长字符串中查找(计数)所有匹配项,尝试在一组较短的字符串中查找 1 个匹配项:vec &lt;- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') )
  • @GregSnow -- 尝试了system.time(a &lt;- grepl("abc", vec))system.time(a &lt;- grepl("abc", vec, fixed=TRUE)),而fixed=TRUE 仍然存在,如果稍微慢一点的话。这些短字符串的差异并不明显,但fixed=TRUE 似乎仍然没有更快。不过,感谢您指出,fixed=TRUE 真正受到打击的是长字符串。
  • grepl(pattern, x) 至少在 2017 年
  • 这不应该是公认的答案,因为值将被解释为正则表达式模式。除非您知道要搜索的字符串不会碰巧看起来像正则表达式模式,否则应始终使用 fixed=TRUE。下面约书亚克里克的回答对此有非常清楚的解释,应该是公认的答案。
【解决方案2】:

回答

唉,我花了 45 分钟才找到这个简单问题的答案。答案是:grepl(needle, haystack, fixed=TRUE)

# Correct
> grepl("1+2", "1+2", fixed=TRUE)
[1] TRUE
> grepl("1+2", "123+456", fixed=TRUE)
[1] FALSE

# Incorrect
> grepl("1+2", "1+2")
[1] FALSE
> grepl("1+2", "123+456")
[1] TRUE

解读

grep 以 linux 可执行文件命名,它本身是 "Global Regular Expression 的首字母缩写Print",它会读取输入行,然后在它们与您提供的参数匹配时打印它们。 “全局”意味着匹配可以出现在输入行的任何位置,我将在下面解释“正则表达式”,但想法是它是匹配字符串的更聪明的方式(R 称之为“字符”,例如class("abc")),和“打印”,因为它是一个命令行程序,发出输出意味着它打印到它的输出字符串。

现在,grep 程序基本上是一个过滤器,从输入行到输出行。似乎 R 的 grep 函数同样会采用一组输入。由于我完全不知道的原因(我大约一个小时前才开始使用 R),它返回匹配索引的向量,而不是匹配列表。

但是,回到你最初的问题,我们真正想要的是知道我们是否在大海捞针中找到了针,一个真/假值。他们显然决定将这个函数命名为grepl,就像在“grep”中一样,但带有一个“Logical”返回值(他们调用真假逻辑值,例如class(TRUE))。

所以,现在我们知道了名称的来源和用途。让我们回到正则表达式。参数,即使它们是字符串,它们也用于构建正则表达式(以下简称:正则表达式)。正则表达式是一种匹配字符串的方法(如果这个定义激怒了你,那就放手吧)。例如,正则表达式a 匹配字符"a",正则表达式a* 匹配字符"a" 0 次或多次,正则表达式a+ 匹配字符"a" 1 次或多次。因此,在上面的示例中,我们正在搜索的针 1+2,当被视为正则表达式时,表示“一个或多个 1 后跟一个 2”......但我们的后面是一个加号!

所以,如果你使用grepl 而不设置fixed,你的针会意外地成为干草堆,并且会经常意外地工作,我们可以看到它甚至适用于 OP 的示例。但这是一个潜在的错误!我们需要告诉它输入是一个字符串,而不是一个正则表达式,这显然是 fixed 的用途。为什么固定?不知道,把这个答案加入书签,b/c 你可能需要再查 5 次才能记住它。

一些最后的想法

您的代码越好,您需要了解的历史就越少。每个参数至少可以有两个有趣的值(否则它不需要是一个参数),文档在这里列出了 9 个参数,这意味着至少有 2^9=512 种调用它的方法,这需要做很多工作编写,测试和记住...解耦这些函数(将它们拆分,消除彼此的依赖关系,字符串事物与正则表达式事物不同,与向量事物不同)。一些选项也是互斥的,不要给用户错误的代码使用方式,即有问题的调用应该是结构上无意义的(例如传递一个不存在的选项),而不是逻辑上无意义的(你必须发出警告来解释它)。打个比方:用一堵墙代替 10 楼一侧的前门比挂一个警告不要使用它的标志要好,但两者都比两者都好。在接口中,函数定义了参数应该是什么样子,而不是调用者(因为调用者依赖于函数,推断每个人可能想要调用它的所有内容都使得函数也依赖于调用者,并且这种类型周期性依赖会迅速阻塞系统,并且永远不会提供您期望的好处)。小心模棱两可的类型,TRUE0"abc" 之类的东西都是向量,这是一个设计缺陷。

【讨论】:

  • 为您的解释干杯!看起来 R 经历了很长一段时间的演变,并且被一些奇怪的设计选择所困扰(例如,参见this question on value types 的答案)。但是,在这种情况下,返回匹配索引向量似乎是合适的,因为 grep 过滤的是行,而不是单元格。
  • “固定”是指匹配“固定”序列的字符。
  • TL;DR: "fixed" 表示搜索模式不应被视为正则表达式。
【解决方案3】:

你想要grepl:

> chars <- "test"
> value <- "es"
> grepl(value, chars)
[1] TRUE
> chars <- "test"
> value <- "et"
> grepl(value, chars)
[1] FALSE

【讨论】:

    【解决方案4】:

    使用stringi包中的这个函数:

    > stri_detect_fixed("test",c("et","es"))
    [1] FALSE  TRUE
    

    一些基准测试:

    library(stringi)
    set.seed(123L)
    value <- stri_rand_strings(10000, ceiling(runif(10000, 1, 100))) # 10000 random ASCII strings
    head(value)
    
    chars <- "es"
    library(microbenchmark)
    microbenchmark(
       grepl(chars, value),
       grepl(chars, value, fixed=TRUE),
       grepl(chars, value, perl=TRUE),
       stri_detect_fixed(value, chars),
       stri_detect_regex(value, chars)
    )
    ## Unit: milliseconds
    ##                               expr       min        lq    median        uq       max neval
    ##                grepl(chars, value) 13.682876 13.943184 14.057991 14.295423 15.443530   100
    ##  grepl(chars, value, fixed = TRUE)  5.071617  5.110779  5.281498  5.523421 45.243791   100
    ##   grepl(chars, value, perl = TRUE)  1.835558  1.873280  1.956974  2.259203  3.506741   100
    ##    stri_detect_fixed(value, chars)  1.191403  1.233287  1.309720  1.510677  2.821284   100
    ##    stri_detect_regex(value, chars)  6.043537  6.154198  6.273506  6.447714  7.884380   100
    

    【讨论】:

      【解决方案5】:

      另外,可以使用“stringr”库:

      > library(stringr)
      > chars <- "test"
      > value <- "es"
      > str_detect(chars, value)
      [1] TRUE
      
      ### For multiple value case:
      > value <- c("es", "l", "est", "a", "test")
      > str_detect(chars, value)
      [1]  TRUE FALSE  TRUE FALSE  TRUE
      

      【讨论】:

        【解决方案6】:

        如果您还想检查一个字符串(或一组字符串)是否包含多个子字符串,您也可以使用“|”在两个子字符串之间。

        >substring="as|at"
        >string_vector=c("ass","ear","eye","heat") 
        >grepl(substring,string_vector)
        

        你会得到

        [1]  TRUE FALSE FALSE  TRUE
        

        因为第一个词有子串“as”,最后一个词含有子串“at”

        【讨论】:

        • OR 运算符正是我所需要的! +1
        【解决方案7】:

        使用grepgrepl 但要注意是否要使用正则表达式

        默认情况下,grep 和相关采用 正则表达式 进行匹配,而不是文字子字符串。如果您没有预料到,并且您尝试匹配无效的正则表达式,则它不起作用:

        > grep("[", "abc[")
        Error in grep("[", "abc[") : 
          invalid regular expression '[', reason 'Missing ']''
        

        要进行真正的子字符串测试,请使用fixed = TRUE

        > grep("[", "abc[", fixed = TRUE)
        [1] 1
        

        如果您确实想要正则表达式,很好,但这不是 OP 似乎要问的。

        【讨论】:

          【解决方案8】:

          您可以使用grep

          grep("es", "Test")
          [1] 1
          grep("et", "Test")
          integer(0)
          

          【讨论】:

            【解决方案9】:

            这里有类似的问题:给定一个字符串和一个关键字列表,检测字符串中包含哪些关键字(如果有)。

            此线程的建议建议 stringrstr_detectgrepl。以下是来自microbenchmark 包的基准测试:

            使用

            map_keywords = c("once", "twice", "few")
            t = "yes but only a few times"
            
            mapper1 <- function (x) {
              r = str_detect(x, map_keywords)
            }
            
            mapper2 <- function (x) {
              r = sapply(map_keywords, function (k) grepl(k, x, fixed = T))
            }
            

            然后

            microbenchmark(mapper1(t), mapper2(t), times = 5000)
            

            我们发现

            Unit: microseconds
                   expr    min     lq     mean  median      uq      max neval
             mapper1(t) 26.401 27.988 31.32951 28.8430 29.5225 2091.476  5000
             mapper2(t) 19.289 20.767 24.94484 23.7725 24.6220 1011.837  5000
            

            如您所见,使用str_detectgrepl 对一个实用的关键字字符串和向量进行超过 5,000 次关键字搜索迭代,grepl 的性能比str_detect 好很多。

            结果是布尔向量r,它标识字符串中包含哪些关键字(如果有)。

            因此,我建议使用grepl 来确定字符串中是否有任何关键字。

            【讨论】:

              猜你喜欢
              • 2014-01-15
              • 2013-08-05
              • 2015-11-12
              • 1970-01-01
              • 2020-11-03
              • 1970-01-01
              相关资源
              最近更新 更多