测试字符串中是否包含字符

Test if characters are in a string

提问人:mike 提问时间:4/13/2012 最后编辑:Amal Kmike 更新时间:9/19/2022 访问量:789335

问:

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

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

如果“”显示为字符串“”的一部分,我想返回。在以下情况下,我想返回 false:TRUEvaluechars

chars <- "test"
value <- "et"
正则表达式 字符串 R-FAQ

评论

17赞 Joshua Cheek 2/1/2017
接受的答案是错误的,您需要添加 ,否则您将将其视为正则表达式而不是字符串。请参阅我 2016 年 10 月的回答。fixed=TRUE
0赞 user3932000 8/1/2019
@JoshuaCheek 除非您的模式中有特殊字符,否则正则表达式将返回与 fixed 相同的结果。
3赞 Joshua Cheek 10/10/2019
当然,但只有当你传递文字时,你才能知道这一点。否则,您将不知道模式中有哪些字符,因此您要么使用,要么有一个错误,它会悄悄地巧妙地弄乱您的数据。fixed=TRUE

答:

43赞 Justin 4/13/2012 #1

你想要:grepl

> chars <- "test"
> value <- "es"
> grepl(value, chars)
[1] TRUE
> chars <- "test"
> value <- "et"
> grepl(value, chars)
[1] FALSE
549赞 smu 4/13/2012 #2

使用函数grepl

grepl( needle, haystack, fixed = TRUE)

这样:

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

用于了解更多信息。?grepl

评论

8赞 Greg Snow 4/13/2012
对于这个简单情况,添加 fixed=TRUE 可能会提高性能(假设您将执行大量此类计算)。
2赞 Greg Snow 4/13/2012
@Josh O'brien 的那篇文章比较了在单个长字符串中查找(计数)所有匹配项,请尝试在一堆较短的字符串中找到 1 个匹配项:.vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') )
3赞 Josh O'Brien 4/13/2012
@GregSnow -- 尝试过了,并且仍然,如果有的话,稍微慢一点。这些短字符串的差异并不明显,但似乎仍然没有更快。不过,感谢您指出,真正的打击是在长弦上。system.time(a <- grepl("abc", vec))system.time(a <- grepl("abc", vec, fixed=TRUE))fixed=TRUEfixed=TRUEfixed=TRUE
2赞 JMR 5/11/2017
grepl(pattern, x) 至少在 2017 年
3赞 bhaller 4/25/2018
这不应该是公认的答案,因为值将被解释为正则表达式模式。fixed=TRUE 应始终使用,除非您知道要搜索的字符串不会碰巧看起来像正则表达式模式。约书亚·克里克(Joshua Creek)下面的回答对此有非常明确的解释,应该是公认的答案。
7赞 nico 4/13/2012 #3

你可以使用grep

grep("es", "Test")
[1] 1
grep("et", "Test")
integer(0)
34赞 bartektartanus 3/14/2014 #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
23赞 C. Zeng 1/25/2015 #5

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

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

你会得到

[1]  TRUE FALSE FALSE  TRUE

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

评论

0赞 sam 10/21/2019
手术室操作员正是我需要的!+1
11赞 Chris 4/14/2016 #6

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

默认情况下,和 related 采用正则表达式进行匹配,而不是文字子字符串。如果您不希望出现这种情况,并且尝试在无效的正则表达式上进行匹配,则它不起作用:grep

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

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

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

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

229赞 Joshua Cheek 10/16/2016 #7

唉,我花了 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")

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

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

所以,现在我们知道这个名字从何而来以及它应该做什么。让我们回到正则表达式。这些参数,即使它们是字符串,它们也用于构建正则表达式(以下称为正则表达式)。正则表达式是一种匹配字符串的方法(如果这个定义激怒了你,那就放手吧)。例如,正则表达式与字符匹配 ,正则表达式与字符匹配 0 次或更多次,正则表达式将与字符匹配 1 次或多次。因此,在上面的例子中,我们正在搜索的针,当被视为正则表达式时,意味着“一个或多个 1 后跟 2”......但是我们的紧随其后的是加号!a"a"a*"a"a+"a"1+2

1+2 as a regex

所以,如果你使用没有设置,你的针会意外地成为大海捞针,而且会经常意外地工作,我们可以看到它甚至适用于 OP 的示例。但这是一个潜在的错误!我们需要告诉它输入是一个字符串,而不是正则表达式,这显然是它的用途。为什么要修复?没有线索,把这个答案加入书签,你可能要再查 5 次才能记住它。greplfixedfixed

最后的几点想法

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

评论

9赞 krevelen 4/25/2017
为您的解释干杯!R似乎是在很长一段时间内演变而来的,并且被一些奇怪的设计选择所困扰(例如,参见关于值类型的这个问题的答案)。但是,在这种情况下,返回匹配索引的向量似乎是合适的,过滤行而不是单元格也是如此。grep
5赞 Will Beason 12/22/2017
“固定”是指与“固定”序列匹配的字符。
3赞 Josiah Yoder 7/15/2021
TL;DR:“固定”表示不应将搜索模式视为正则表达式。
2赞 IRTFM 7/4/2022
如果您阅读“?grep”页面,您会发现 value=TRUE,使其按照您天真的预期运行。不停地谈论它如何表现得不像你所期望的那样,只是意味着你在开始咆哮之前没有阅读文档。
40赞 Surya 6/14/2017 #8

此外,可以使用“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

评论

0赞 sfuqua 4/17/2023
stringer 和 grepl 和 grep 中的 str_detect 函数有什么区别中的好处的良好讨论?stringr
1赞 Alex L 6/8/2020 #9

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

此线程的建议建议 's 和 .以下是该软件包中的基准测试:stringrstr_detectgreplmicrobenchmark

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

如您所见,使用和处理实际的关键字字符串和向量进行超过 5,000 次关键字搜索迭代,其性能比 .str_detectgreplgreplstr_detect

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

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