测试字符是否在字符串中

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

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

我想返回TRUE,如果"作为字符串“__abc2”的一部分出现。在下面的场景中,我想返回false:

chars <- "test"
value <- "et"
715813 次浏览

你需要grepl:

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

使用grepl函数

grepl( needle, haystack, fixed = TRUE)

像这样:

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

使用?grepl来查找更多信息。

你可以使用grep

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

使用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

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

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

你会得到

[1]  TRUE FALSE FALSE  TRUE

因为第一个单词包含子字符串“as”,而最后一个单词包含子字符串“at”

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

默认情况下,grep和相关参数接受一个正则表达式来匹配,而不是一个文字子字符串。如果你没有预料到,并且你试图匹配一个无效的正则表达式,它就不起作用:

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

要做一个真正的子字符串测试,使用fixed = TRUE

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

如果你确实想要正则表达式,很好,但这并不是OP所要求的。

回答

唉,我花了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”中一样,但返回值是“__abc2logical”(他们调用true和false逻辑值,例如class(TRUE))。

所以,现在我们知道这个名字的来源和它的作用了。让我们回到正则表达式。虽然参数是字符串,但它们被用于构建正则表达式(以下简称为regex)。正则表达式是一种匹配字符串的方法(如果这个定义让你生气,那就放弃它)。例如,正则表达式a匹配字符"a",正则表达式a*匹配字符"a" 0次或更多次,而正则表达式a+将匹配字符"a" 1次或更多次。因此,在上面的例子中,我们正在搜索1+2的指针,当被视为正则表达式时,意味着“一个或多个1后跟一个2”…但是我们的后面是一个加号!

1+2 as a regex

因此,如果你使用grepl而没有设置fixed,你的针会意外地变成干草堆,这将经常意外地工作,我们可以看到它甚至适用于OP的例子。但这是一个潜在的漏洞!我们需要告诉它输入是一个字符串,而不是一个正则表达式,这显然是fixed的目的。为什么固定?不知道,把这个答案收藏起来,你可能要再查5次才能记住它。

最后的一些想法

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

同样,也可以做使用"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

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

这个线程的建议是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对关键字的实际字符串和向量进行了超过5000次的关键字搜索,grepl的性能比str_detect要好得多。

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

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