用于正则表达式捕获组的R函数是什么?

8

我正在使用R语言处理文本,对于某些特定的提取需求,我需要使用捕获组。但出于某种原因,我熟悉的基础/stringr函数似乎不支持捕获组:

str_extract("abcd123asdc", pattern = "([0-9]{3}).+$") 
# Returns: "123asdc"

stri_extract(str = "abcd123asdc", regex = "([0-9]{3}).+$")
# Returns: "123asdc"

grep(x = "abcd123asdc", pattern = "([0-9]{3}).+$", value = TRUE)
# Returns: "abcd123asdc"

常规的“R捕获组正则表达式”谷歌搜索没有提供有用的解决方案。我是错过了什么,还是在R中未实现捕获组? 编辑:尝试评论中提出的解决方案后,它适用于小例子,但对于我的情况失败了。 请注意,这是来自enron电子邮件数据集的文本,不包含敏感信息。
txt <- "Message-ID: <24216240.1075855687451.JavaMail.evans@thyme>
Date: Wed, 18 Oct 2000 03:00:00 -0700 (PDT)
From: phillip.allen@enron.com
To: leah.arsdall@enron.com
Subject: Re: test
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
X-From: Phillip K Allen
X-To: Leah Van Arsdall
X-cc: 
X-bcc: 
X-Folder: \\Phillip_Allen_Dec2000\\Notes Folders\\sent mail   
X-Origin: Allen-P
X-FileName: pallen.nsf

test successful.  way to go!!!"

sub("X-FileName:.+\n\n([\\W\\w]+)$", "\\1", txt)
# Returns all of "txt", not the capture group

由于我们只有一个捕获组,应该用"\1"来捕获它,我已经在在线正则表达式测试器上测试过了,应该可以工作。也尝试使用\n和\n来表示换行符。有什么想法吗?


sub(".*([0-9]{3}.+$)", "\\1", "abcd123asdc") 可能会返回:123asdc - David Arenburg
@hwnd:实际的正则表达式不太容易显式匹配,但这个例子更容易输入。 - BallzofFury
@David Arenburg:太棒了,看起来可以工作! - BallzofFury
请参阅gregexpr - David Arenburg
@BallzofFury:在你的输入文本中,\P\N是未知的转义序列,反斜杠必须加倍。 - Wiktor Stribiżew
regmatches 用于在 R 中仅提取匹配项,与 g/regexprregexec 结合使用。它在 ?grep 的“另请参阅”部分中列出并描述 - 帮助文件总是值得一读的。 - thelatemail
1个回答

8

完成任务

你可以始终使用 stringr 来提取捕获组,使用 str_matchstr_match_all:

> result <- str_match(txt, "X-FileName:.+\n\n(?s)(.+)$")
> result[,2]
[1] "test successful.  way to go!!!"

模式详细信息:

  • X-FileName: - 一个字面子字符串
  • .+ - 任何一个1个或多个字符,除了换行符(因为在ICU正则表达式中,点不匹配换行符)
  • \n\n - 2个换行符号
  • (?s) - 内联DOTALL修改器(现在右侧的.将匹配换行符号)
  • (.+) - Group 1捕获任何1个或多个字符(包括换行符),直到
  • $ - 字符串的结尾。

或者您可以使用基本R regmatchesregexec

> result <- regmatches(txt, regexec("X-FileName:[^\n]+\n\n(.+)$", txt))
> result[[1]][2]
[1] "test successful.  way to go!!!"

请参见在线R演示。在这里,使用了TRE正则表达式(不幸的是,无法使用PCRE正则表达式),因此将匹配包括换行符在内的任何字符,因此模式看起来像X-FileName:[^\n]+\n\n(.+)$
  • X-FileName: - 字面字符串
  • [^\n]+ - 除换行符外的1个或多个字符
  • \n\n - 2个换行符
  • (.+) - 任何1个或多个字符(包括换行符),尽可能多地匹配,直到
  • $ - 字符串结束。
还可以考虑sub选项:
sub(".*X-FileName:[^\n]+\n\n", "", txt)
[1] "test successful.  way to go!!!"

请看这个R演示 。在这里,.*匹配任意0+个字符,尽可能多地匹配整个字符串,然后回溯查找X-FileName:子字符串,[^ \n]+匹配除了换行符之外的一个或多个字符,然后\n\n匹配2个换行符。

性能比较

考虑到hwnd的评论,我添加了基于TRE正则表达式的sub选项,它似乎是所有建议中最快的。使用str_match几乎与我的上面的sub代码一样快:

library(microbenchmark)

f1 <- function(text) { return(str_match(txt, "X-FileName:.+\n\n(?s)(.+)$")[,2]) }
f2 <- function(text) { return(regmatches(txt, regexec("X-FileName:[^\n]+\n\n(.+)$", txt))[[1]][2]) }
f3 <- function(text) { return(sub('(?s).*X-FileName:[^\n]+\\R+', '', txt, perl=TRUE)) }
f4 <- function(text) { return(sub('.*X-FileName:[^\n]+\n\n', '', txt)) }

> test <- microbenchmark( f1(txt), f2(txt), f3(txt), f4(txt), times = 500000 )
> test
Unit: microseconds
    expr    min     lq     mean median     uq       max neval  cld
 f1(txt) 21.130 24.451 28.08150 27.168 28.677 53796.565 5e+05  b  
 f2(txt) 29.280 32.903 37.46800 35.318 37.431 54556.635 5e+05   c 
 f3(txt) 57.655 59.466 63.36906 60.674 61.881  1651.448 5e+05    d
 f4(txt) 22.036 23.545 25.56820 24.451 25.356  1660.504 5e+05 a   

或者 sub('(?s).*X-FileName:[^\n]+\\R+', '', txt, perl=TRUE) ... https://codebunk.com/pb/852138641 - hwnd
@hwnd:事实证明,使用sub的类似TRE正则表达式性能更好。 - Wiktor Stribiżew
1
非常感谢,这个很好用!我之前不清楚捕获组在结果的第二个索引中返回,这导致了我的困惑。我认为你的评论会帮助很多人! - BallzofFury

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接