R中的重叠匹配

14

我进行了搜索,并找到了这个论坛讨论,以实现重叠匹配的效果。

我还发现以下SO问题,涉及查找索引以执行此任务,但未能找到有关在R语言中抓取重叠匹配的简明信息。

我可以在支持(PCRE)的任何语言中执行此任务,通过在前瞻内实现捕获组来捕获重叠匹配。

但是,在R中使用perl=T实际执行相同的操作时,没有结果产生。

> x <- 'ACCACCACCAC'
> regmatches(x, gregexpr('(?=([AC]C))', x, perl=T))[[1]]
[1] "" "" "" "" "" "" ""

同样适用于同时使用 stringistringr 包。
> library(stringi)
> library(stringr)
> stri_extract_all_regex(x, '(?=([AC]C))')[[1]]
[1] "" "" "" "" "" "" ""
> str_extract_all(x, perl('(?=([AC]C))'))[[1]]
[1] "" "" "" "" "" "" ""

执行此操作应返回以下正确结果:
[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"

编辑

  1. 我非常清楚regmatches在捕获匹配方面的表现不佳,但是究竟是什么原因导致了这种行为,为什么没有返回结果?我正在寻找一个比较详细的答案

  2. stringistringr包是否不能像regmatches那样执行此操作?

  3. 请随意添加我的答案或提出一个与我发现的不同的解决方法。

6个回答

7

至于解决方法,这是我想出来提取重叠匹配的方案。

> x <- 'ACCACCACCAC'
> m <- gregexpr('(?=([AC]C))', x, perl=T)
> mapply(function(X) substr(x, X, X+1), m[[1]])
[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"

请随意添加或评论更好的执行此任务的方法。

这个解决方案的问题在于,它只适用于捕获区域始终为2个字符的情况。更通用的解决方案是: - Ken Williams
哎呀,我忘了不能在评论中放置代码块。我会将其作为单独的答案发布。 - Ken Williams

7
标准的regmatches在处理捕获匹配时效果不佳(特别是在同一字符串中有多个捕获匹配的情况下)。而且,在这种情况下,由于你正在“匹配”一个前瞻(忽略捕获),所以匹配本身的长度为零。还有一个可能会说明这一点的regmatches()<-函数。请注意。
x <- 'ACCACCACCAC'
m <- gregexpr('(?=([AC]C))', x, perl=T)
regmatches(x, m) <- "~"
x
# [1] "~A~CC~A~CC~A~CC~AC"

请注意所有字母都被保留了,我们只是用可以观察到的东西替换了零长度匹配的位置。
我创建了一个regcapturedmatches()函数,我经常用它来完成这样的任务。例如:
x <- 'ACCACCACCAC'
regcapturedmatches(x, gregexpr('(?=([AC]C))', x, perl=T))[[1]]

#      [,1] [,2] [,3] [,4] [,5] [,6] [,7]
# [1,] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
< p > gregexpr 很好地获取了所有数据,因此您可以从该对象中提取它,无论您是否愿意使用此辅助函数。 < /p >

你创建了一个有趣的函数。我很清楚零宽度匹配,所以基本上regmatches和其他包(如stringi、r)都不能处理这个问题? - hwnd
我个人没有使用过stringr,所以无法对其进行评论。但是,regmatches 真正专注于匹配而不是捕获(尽管它们高度相关但略有不同)。我已经添加了一个额外的示例,试图清楚地说明 regmatches() 捕获的内容与我的函数有何不同。 - MrFlick
是的,我之前也使用过 regmatches()<- 来观察零宽度匹配的效果。 - hwnd

5

使用前瞻部分中的捕获组的 stringi 解决方案:

> stri_match_all_regex('ACCACCACCAC', '(?=([AC]C))')[[1]][,2]
## [1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"    

奇怪,为什么使用stri_extract_all_regex无法正常工作? - hwnd
@hwnd:这是一个0长度匹配;(?=...)不会推进输入位置。不会 - gagolews
是的,我知道这是一个零宽度匹配 =) 我猜extract_all_regexmatch_all_regex之间有所不同。 - hwnd
不,结果矩阵的第一列(整个匹配)仅由空字符串组成 :) - gagolews
好的,现在我明白并理解你的意思了。 - hwnd

4

提取相同信息的另一种方法是用"capture.length"替换"match.length",这是我过去常用的方法之一:

x <- c("ACCACCACCAC","ACCACCACCAC")
m <- gregexpr('(?=([AC]C))', x, perl=TRUE)
m <- lapply(m, function(i) {
       attr(i,"match.length") <- attr(i,"capture.length")
       i
     })
regmatches(x,m)

#[[1]]
#[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"
#
#[[2]]
#[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"

+1 感谢您提供的额外解决方案。我曾经使用 capture.startcapture.length 进行过类似操作。 - hwnd

4

这不是一个正则表达式的解决方案,也不能真正回答你更重要的问题,但你也可以通过使用两个字符的子字符串,然后去掉不需要的CA元素来获得所需的结果。

x <- 'ACCACCACCAC'
y <- substring(x, 1:(nchar(x)-1), 2:nchar(x))
y[y != "CA"]
# [1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"

1

根据@hwnd的答案(原始答案不允许变长捕获区域),这是一个额外的答案,只使用内置的R函数:

> x <- 'ACCACCACCAC'
> m <- gregexpr('(?=([AC]C))', x, perl=T)[[1]]
> start <- attr(m,"capture.start")
> end <- attr(m,"capture.start") + attr(m,"capture.length") - 1
> sapply(seq_along(m), function(i) substr(x, start[i], end[i]))
[1] "AC" "CC" "AC" "CC" "AC" "CC" "AC"

“Pretty ugly”,这就是为什么存在着stringr等包的原因。


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