使用stringi提取命名捕获组:作为无名捕获组首子项时名称丢失
R stringi 命名捕获组丢失名称的问题处理
当使用R的stringi包(基于ICU正则引擎)提取命名捕获组时,若命名组作为外层无名捕获组的第一个子项,会出现命名丢失、仅保留序号索引的情况,以下是具体分析和解决方法:
问题复现
直接运行示例代码可观察到现象:
library(stringi) stri_locate_all_regex("ab", "((?<letterone>[a-z])(?<lettertwo>[a-z]))", capture_groups = TRUE) #> [[1]] #> start end #> [1,] 1 2 #> attr("capture_groups") #> attr("capture_groups")[[1]] #> start end #> [1,] 1 2 #> #> attr("capture_groups")[[2]] #> start end #> [1,] 1 1 #> #> attr("capture_groups")$lettertwo #> start end #> [1,] 2 2
可以看到,letterone组仅以序号[[2]]存在,丢失了命名标识;但如果在外层组内添加无操作前置项(如a{0}),命名组就能正常显示:
stri_locate_all_regex("ab", "(a{0}(?<letterone>[a-z])(?<lettertwo>[a-z]))", capture_groups = TRUE) #> [[1]] #> start end #> [1,] 1 2 #> attr("capture_groups") #> attr("capture_groups")[[1]] #> start end #> [1,] 1 2 #> #> attr("capture_groups")$letterone #> start end #> [1,] 1 1 #> #> attr("capture_groups")$lettertwo #> start end #> [1,] 2 2
原因说明
这是stringi与ICU正则引擎交互时的已知边缘场景行为:当命名捕获组是外层无名捕获组的第一个子组件时,stringi的capture_groups属性会优先用序号索引覆盖命名标识,而非同时保留两种索引方式。
解决方案
1. 将外层无名组改为非捕获组
如果不需要外层捕获组的匹配结果,直接把(...)改成非捕获组(?:...),这样外层组不会占用序号,命名组就能正常被识别:
stri_locate_all_regex("ab", "(?:(?<letterone>[a-z])(?<lettertwo>[a-z]))", capture_groups = TRUE) #> [[1]] #> start end #> [1,] 1 2 #> attr("capture_groups") #> attr("capture_groups")$letterone #> start end #> [1,] 1 1 #> #> attr("capture_groups")$lettertwo #> start end #> [1,] 2 2
2. 添加无意义前置占位符
如果必须保留外层捕获组,可在外层组内添加一个不影响匹配的空非捕获组(?:)作为前置项,让命名组不再是第一个子项:
stri_locate_all_regex("ab", "((?:)(?<letterone>[a-z])(?<lettertwo>[a-z]))", capture_groups = TRUE)
3. 使用stri_match_all_regex提取命名组
stri_match_all_regex返回的结果会将命名捕获组作为列名,更稳定,可直接通过列名提取:
matches <- stri_match_all_regex("ab", "((?<letterone>[a-z])(?<lettertwo>[a-z]))")[[1]] letterone <- matches[, "letterone"] lettertwo <- matches[, "lettertwo"]
结论
该现象并非严格意义上的Bug,属于stringi处理嵌套捕获组时的特殊逻辑,通过调整正则结构或换用其他提取函数即可解决。
内容的提问来源于stack exchange,提问作者Erik A
相关产品推荐
相关产品推荐

