提取可能出现多次或根本不出现的字符串元素

Posted

技术标签:

【中文标题】提取可能出现多次或根本不出现的字符串元素【英文标题】:Extract string elements that possibly appear multiple times, or not at all 【发布时间】:2014-08-10 14:22:01 【问题描述】:

从 URL 的字符向量开始。目标是仅以公司名称结束,这意味着在下面的示例中,一列仅包含 "test""example""sample"

urls <- c("http://grand.test.com/", "https://example.com/", 
          "http://.big.time.sample.com/")

删除".com" 及其后面的任何内容并保留第一部分:

urls <- sapply(strsplit(urls, split="(?<=.)(?=\\.com)", perl=T), "[", 1) 

urls
# [1] "http://grand.test"    "https://example"      "http://.big.time.sample"

我的下一步是使用链接的gsub() 调用删除http://https:// 部分:

urls <- gsub("^http://", "",  gsub("^https://", "", urls))

urls
# [1] "grand.test"       "example"          ".big.time.sample"

但这里是我需要帮助的地方。如何处理网址的第一个和第三个字符串中公司名称前的多个句点(点)?例如,下面的调用为第二个字符串返回 NA,因为 "example" 字符串没有剩余的句点。或者如果我只保留第一部分,我会失去公司名称。

urls  <- sapply(strsplit(urls, split = "\\."), "[", 2)
urls
# [1] "test" NA     "big"

urls  <- sapply(strsplit(urls, split = "\\."), "[", 1)
urls
# [1] "grand"   "example" ""  

也许是一个ifelse() 调用,它计算剩余的周期数,并且仅在有多个周期时才使用 strsplit?另请注意,公司名称前可能有两个或多个句点。我不知道如何进行环视,这可能会解决我的问题。但这并没有

strsplit(urls, split="(?=\\.)", perl=T)

感谢您的任何建议。

【问题讨论】:

我是新手。我喜欢这两个答案,user20650 的简单性,但在 agstudy 中包含在 http(s) 步骤中。我应该选择一个和一个只单击复选标记以获得答案吗?我应该再等一会儿吗? 可能最好等待,因为现场有一些正则表达式用户,您可能会得到一个更简单的答案。 [但是是的,请单击您最喜欢的答案旁边的箭头,但您可以随意投票] 【参考方案1】:

这是一种可能比其他一些方法更容易理解和概括的方法:

pat = "(.*?)(\\w+)(\\.com.*)"
gsub(pat, "\\2", urls)

它的工作原理是将每个字符串分成三个捕获组,它们一起匹配整个字符串,然后替换回您想要的捕获组(2)

pat = "(.*?)(\\w+)(\\.com.*)"
#        ^    ^       ^
#        |    |       |
#       (1)  (2)     (3)  

编辑(添加?修饰符的解释)

请注意,捕获组 (1) 需要包含“不贪婪”或“最小”量词 ? (also sometimes called "lazy" or "reluctant")。它本质上是告诉正则表达式引擎匹配尽可能多的字符......而不用尽任何可能成为以下捕获组(2)的一部分的字符。

没有尾随?,重复量词默认是贪婪的;在这种情况下,一个贪婪的捕获组(.*),因为它匹配任意数量的任何类型的字符,将“吃掉”字符串中的所有字符,而其他两个捕获组根本没有留下任何字符——这不是一种行为我们想要!

【讨论】:

不错的一个。非常好。 伟大的乔希,我一直在努力,但没有成功。您能否解释一下第一个术语是如何指定的,请(.*?)。谢谢 @user20650 -- 完成!即使添加的解释本身并不能让您一直走到那里,它至少会为您提供搜索词(“贪婪”和“最小”),让您可以在剩下的时间里跟踪它。 非常感谢,你真好。很好的解释,也感谢关键字。【参考方案2】:

我认为应该有更简单的,但这有效:

 sub('.*[.]','',sub('https?:[/]+[.]?(.*)[.]com[/]','\\1',urls))
 [1] "test"    "example" "sample" 

“urls”是你的第一个 url 的向量。

【讨论】:

【参考方案3】:

我认为有一种方法可以只提取 '.com` 之前的单词,但可能会给出一个想法

sub(".com", "", regmatches(urls, gregexpr("(\\w+).com", urls)))

【讨论】:

【参考方案4】:

使用strsplit 可能也值得一试:

sapply(strsplit(urls,"/|\\."),function(x) tail(x,2)[1])
#[1] "test"    "example" "sample"

【讨论】:

【参考方案5】:

这是一个很好的例子。有用的答案和一些解释很快就生成了。

回答我自己的问题并不能描述我在做什么。我想感谢贡献者,回馈一些可能会帮助其他看到这个问题的人,并解释我为什么选择一个答案。评论似乎不正确,也不够长。

以下将每个答案与我的(谦虚的,很高兴得到纠正)解释一起汇总,其中一些解释包含回答者的解释。仔细研究答案教会了我很多东西,并帮助我做出了首选答案的选择。其他人使用非 base-R 函数,这是一个创建的函数,它可能非常棒,但并不那么容易获得。我喜欢第二个答案,因为它只使用了子函数,但我把月桂花环给了第五个,因为它优雅地使用了我很高兴学习的两种技术。谢谢大家。

ANS 1

sub(".com", "", regmatches(urls, gregexpr("(\\w+).com", urls)))

gregexpr“.com” 之前使用特殊字符“w+” 查找任何一个或多个单词,并返回一个包含长度和使用字节数的列表

regmatches 获取 gregexpr 找到的内容并仅返回已识别的字符串

sub 从每个字符串中删除第一个“.com”[我不知道为什么 gsub 不起作用,但当您只想要第一个实例时,全局 sub 可能是一种风险]

ANS 2

sub('.*[.]','', sub('https?:[/]+[.]?(.*)[.]com[/]','\\1',urls))

内部子句通过问号特殊字符 ? 处理“http:”和“https:”,这允许“s”是可选的

内部子函数然后处理一个或多个“/”,其字符类仅包含一个正斜杠但由“+” 扩展,即在http://

右边的内部子正则表达式的下一部分包括任意数量的字符,作为“[.]?的可选字符

接下来,将“com”之前的句点放在括号中而不是转义

然后是“com”,后跟一个正斜杠 [我不确定我是否理解那部分]

“’\\1’ 仅保留子函数提取的内容的第一部分

以上所有返回:

[1] "grand.test"      "example"         "big.time.sample"

最左边的子函数获取内部子函数的结果,并删除括号中句点前带有“.*”的所有字符

ANS 3

sapply(strsplit(urls, "/|\\."), function(x) tail(x,2)[1])

首先,strsplit 使用正斜杠或句点分隔每个字符串,使用竖线 |产生一个列表

[[1]]
[1] "http:" ""      "grand" "test"  "com"  

[[2]]
[1] "https:"  ""        "example" "com"    

[[3]]
[1] "http:"  ""       ""       "big"    "time"   "sample" "com"

接下来,匿名函数使用tail 函数查找每个字符串中的最后两个元素,并选择第一个元素,从而巧妙地消除每个“.com”

用 sapply 函数包装这两个步骤,将匿名函数的操作向量化到所有三个字符串

ANS 4

library(stringr)
word(basename(urls), start = -2, sep = "\\.")

basename 函数返回

[1] "grand.test.com"       "example.com"          ".big.time.sample.com"

basename() 的帮助中,我们了解到“basename 删除了所有路径,包括最后一个路径分隔符(如果有)”这巧妙地删除了 http:// 和 https:// 元素。

然后,word() 函数通过使用否定运算符 (start = -2) 从末尾获取第二个“单词”,假设分隔符是 . (句号)(sep = "\." )。

ANS 5

pat = "(.*?)(\\w+)(\\.com.*)"
gsub(pat, "\\2", urls)

分配给对象“pat”的正则表达式将每个字符串分成三个捕获组,它们一起匹配整个字符串

使用gsub 函数,搜索“pat”字符串,它会在捕获组 (2) 中替换回所需部分。

注意这里的两个技巧:用你的表达式创建一个对象,然后在正则表达式中使用它。这种方法有助于保持代码更简洁和更易于阅读——正如 gsub 调用行所证明的那样。其次,注意捕获组的使用,它们是括在括号中的正则表达式的组件。它们可以在以后使用,如本例中的“'\2'”

pat = "(.*?)(\\w+)(\\.com.*)"
#        ^    ^       ^
#        |    |       |
#       (1)  (2)     (3)  

ANS 6

regcapturedmatches(urls, regexpr("([^.\\/]+)\\.com", urls, perl=T))

这可能是一个很好的解决方案,但它依赖于一个函数 regcapturematches,它不在基础 R 或其他包中,例如 qdapstringistringr

先生。 Flick 提出了一个很好的观点,“如果你只想要一个简单的向量作为返回值,你可以 unlist() 结果。”

他解释说,“该模式的想法是在“.com”之前抓住不是点或“/”的所有内容。”那是括号中的表达式,用+号表示它可以是多个。

Perl = T 似乎是所有正则表达式的一个很好的论据

【讨论】:

【参考方案6】:

因为你从来没有足够的正则表达式选项,这里有一个使用regcapturedmatches.R 函数的选项

regcapturedmatches(urls, regexpr("([^.\\/]+)\\.com", urls, perl=T))

如果你只想要一个简单的向量作为返回值,你可以unlist() 结果。该模式的想法是在“.com”之前抓住不是点或“/”的所有内容。

【讨论】:

【参考方案7】:

您可以使用stringr::word()basename()

basename() 在处理 URL 时非常方便。

> library(stringr)
> word(basename(urls), start = -2, sep = "\\.")
# [1] "test"    "example" "sample"  

basename(urls)给了

[1] "grand.test.com"       "example.com"          ".big.time.sample.com"

然后,在word() 函数中,假设分隔符为.sep = "\\."),我们取最后的第二个单词(start = -2)。

【讨论】:

您能否详细说明您的答案? 6k;到现在为止,您应该知道纯代码的答案是不可接受的。 不错的答案。不知道这些功能中的任何一个

以上是关于提取可能出现多次或根本不出现的字符串元素的主要内容,如果未能解决你的问题,请参考以下文章

shell匹配字符串,一行中出现多次(grep或sed)

Python 正则表达式零次或多次出现

+ * ?

javascript 匹配出现零次或多次的字符

javascript 匹配出现一次或多次的字符

某字符串可能包含26个英文字母,可能包含6种符号,可能包含3个数字,统计他们出现的个数