测试字符是否在字符串中


279

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

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

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

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

12
接受的答案是错误的,您需要添加fixed=TRUE,否则会将其视为正则表达式而不是字符串。请参阅我从2016
Joshua Cheek '02

@JoshuaCheek除非您的模式中包含特殊字符,否则regex将返回与固定相同的结果。
user3932000 '19

1
可以,但是只有在传递文字时,您才能知道。否则,您将不知道该模式中包含哪些字符,因此您要么使用它,要么fixed=TRUE有一个错误地将数据悄悄地弄乱了。
约书亚脸颊

Answers:


388

使用 grepl功能

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

使用?grepl以了解更多信息。


8
对于这种简单的情况,添加fixed = TRUE可能会提高性能(假设您将进行很多此类计算)。
格雷格·斯诺

1
@Josh O'brien,该帖子比较了在单个长字符串中查找(计算)所有匹配项,尝试在一串较短的字符串中查找1个匹配项:vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') )
格雷格·斯诺

2
@GregSnow-尝试过system.time(a <- grepl("abc", vec))system.time(a <- grepl("abc", vec, fixed=TRUE))fixed=TRUE如果仍然有些慢的话,它仍然是。这些短字符串之间的区别并不明显,但fixed=TRUE似乎并没有更快。但是,感谢您指出,它是在长字符串上fixed=TRUE产生了真正的成功。
乔什·奥布莱恩

2
grepl(pattern,x)至少在2017年
JMR,

2
这不应是公认的答案,因为值将被解释为正则表达式模式。除非您知道所搜索的字符串不会看起来像正则表达式模式,否则始终应使用fixed = TRUE。约书亚·克里克(Joshua Creek)在下面的回答对此有很清楚的解释,应该被接受。
bhaller

159

回答

igh,我花了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

解释

grepLinux的可执行文件的名字命名,这本身就是一个缩写“ 叶形[R egular é上的表达P RINT”,它会读取输入的线,然后打印出来,如果他们匹配你给的参数。“全局”表示匹配可以在输入行中的任何地方进行,我将在下面解释“正则表达式”,但是这种想法是匹配字符串的一种更聪明的方式(R称为“字符”,例如class("abc"))和“打印” ”,因为它是一个命令行程序,所以发出输出意味着它会打印到其输出字符串。

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

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

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

1 + 2作为正则表达式

因此,如果使用了grepl不带设置fixed,那么针会意外地变成干草堆,并且偶然会经常出现,我们可以看到它甚至适用于OP的示例。但这是一个潜在的错误!我们需要告诉它输入是一个字符串,而不是正则表达式,这显然fixed是为了什么。为什么要解决?毫无头绪,将此答案添加为书签b / c,您可能需要再查找5次才能记住它。

最后的几点思考

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


6
为您的解释加油!R似乎在很长一段时间内不断发展,并停留在一些奇怪的设计选择上(例如,关于值类型的问题的答案)。但是,在这种情况下,返回匹配索引的向量似乎是合适的,就像grep过滤行而不是单元格一样。
krevelen

4
“固定”是指与“固定”序列匹配的字符。
Will Beason

32

您要grepl

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

27

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

22

另外,可以使用“字符串”库来完成

> 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

20

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

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

你会得到

[1]  TRUE FALSE FALSE  TRUE

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


OR运算符正是我所需要的!+1
山姆

10

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

默认情况下,grep与related相关的是正则表达式,而不是文字子字符串。如果您不希望这样,并且尝试匹配无效的正则表达式,那么它将不起作用:

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

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

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

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


7

您可以使用 grep

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

0

此处存在类似的问题:给定字符串和关键字列表,请检测该字符串中包含哪些关键字(如果有)。

从这个线程建议提出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在实际的关键字字符串和向量上grepl进行了5,000次以上的关键字搜索迭代,其效果要好于str_detect

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.