测试字符是否在字符串中

mik*_*ike 248 regex string r r-faq

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

chars <- "test"
value <- "es"
Run Code Online (Sandbox Code Playgroud)

如果"value"作为字符串"chars"的一部分出现,我想返回TRUE.在以下场景中,我想返回false:

chars <- "test"
value <- "et"
Run Code Online (Sandbox Code Playgroud)

小智 345

使用该 grepl功能

grepl(value, chars)
# TRUE
Run Code Online (Sandbox Code Playgroud)

  • 对于这个简单的情况,添加fixed = TRUE可以提高性能(假设您将进行大量的这些计算). (8认同)
  • @Josh O'brien,那篇文章比较了在单个长字符串中查找(计数)所有匹配项,尝试在一堆较短的字符串中查找 1 个匹配项:`vec &lt;-replicate(100000,paste(sample(letters,10,replace) =真),折叠=''))`。 (2认同)
  • @GregSnow - 试过`system.time(一个< - grepl("abc",vec))`和`system.time(一个< - grepl("abc",vec,fixed = TRUE))`和`修复= TRUE`仍然是,如果有点慢.这些短字符串的差异并不明显,但"fixed = TRUE"似乎仍然不会更快.但是,感谢你指出,它是在长字符串上,`fixed = TRUE`取得真正的打击. (2认同)
  • grepl(pattern,x)至少在2017年 (2认同)
  • 这不应该是可接受的答案,因为值将被解释为正则表达式模式.应始终使用fixed = TRUE,除非您知道要搜索的字符串看起来不像正则表达式模式.Joshua Creek在下面的回答对此有一个非常明确的解释,应该是公认的答案. (2认同)

Jos*_*eek 139

回答

叹了口气,我花了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
Run Code Online (Sandbox Code Playgroud)

解释

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

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

但是,回到你原来的问题,我们真正想要的是知道我们是否在大海捞针中找到了针,一个真/假值.他们显然决定命名这个功能grepl,如"grep的",而是用" 大号 ogical"返回值(他们称之为真假逻辑值,例如class(TRUE)).

所以,现在我们知道这个名字来自何处以及它应该做什么.让我们回到正则表达式.参数,即使它们是字符串,它们也用于构建正则表达式(以下称为正则表达式).正则表达式是一种匹配字符串的方法(如果这个定义让你烦恼,那就让它去).例如,正则表达式a匹配字符"a",正则表达式a*匹配字符"a"0次或更多次,正则表达式a+将匹配字符"a"1次或更多次.因此,在上面的例子中,我们正在寻找的针1+2,当被视为正则表达式时,意味着"一个或多个1后跟一个2"......但是我们的针后面加了一个!

1 + 2作为正则表达式

所以,如果你使用grepl没有设置fixed,你的针将不小心成为干草堆,这将不经常偶然地工作,我们可以看到它甚至适用于OP的例子.但这是一个潜在的错误!我们需要告诉它输入是一个字符串,而不是一个正则表达式,这显然fixed是为了什么.为何固定?毫无头绪,给这个答案添加书签b/c你可能需要再记录5次才能记住它.

最后一点想法

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

  • 干杯为您解释!似乎R经过很长一段时间的演变而陷入了一些奇怪的设计选择(参见[关于价值类型的这个问题的答案](http://stackoverflow.com/questions/8855589)).但是,在这种情况下返回匹配索引的向量似乎是合适的,因为`grep`是过滤行而不是单元格. (5认同)
  • "fixed"是指与"固定"序列匹配的字符. (4认同)
  • TL;DR:“固定”意味着搜索模式不应被视为正则表达式。 (3认同)
  • 如果您阅读“?grep”页面,您会发现 value=TRUE 使其表现得如您天真地期望的那样。不断地谈论它的行为与您期望的不符,这只意味着您在开始咆哮之前没有阅读文档。 (2认同)

Jus*_*tin 32

你想要grepl:

> chars <- "test"
> value <- "es"
> grepl(value, chars)
[1] TRUE
> chars <- "test"
> value <- "et"
> grepl(value, chars)
[1] FALSE
Run Code Online (Sandbox Code Playgroud)


bar*_*nus 25

stringi包中使用此功能:

> stri_detect_fixed("test",c("et","es"))
[1] FALSE  TRUE
Run Code Online (Sandbox Code Playgroud)

一些基准:

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
Run Code Online (Sandbox Code Playgroud)


Sur*_*rya 18

另外,可以使用"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
Run Code Online (Sandbox Code Playgroud)


C. *_*eng 17

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

>substring="as|at"
>string_vector=c("ass","ear","eye","heat") 
>grepl(substring,string_vector)
Run Code Online (Sandbox Code Playgroud)

你会得到

[1]  TRUE FALSE FALSE  TRUE
Run Code Online (Sandbox Code Playgroud)

因为第一个单词有子串"as",最后一个单词包含子串"at"


Chr*_*ris 9

使用grepgrepl 但是要知道您是否要使用正则表达式.

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

> grep("[", "abc[")
Error in grep("[", "abc[") : 
  invalid regular expression '[', reason 'Missing ']''
Run Code Online (Sandbox Code Playgroud)

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

> grep("[", "abc[", fixed = TRUE)
[1] 1
Run Code Online (Sandbox Code Playgroud)

如果你确实想要正则表达式,那很好,但这不是OP似乎要问的问题.


nic*_*ico 7

您可以使用 grep

grep("es", "Test")
[1] 1
grep("et", "Test")
integer(0)
Run Code Online (Sandbox Code Playgroud)