R语言gsub处理超长正则表达式报错的解决方法
R超长正则gsub报错解决方案
问题场景
需要对长字符串应用超长正则表达式模式,构造长正则的示例代码如下:
set.seed(1234) myFun <- function(n = 5000) { a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE)) paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE)) } long_regex <- paste0(myFun(1000), collapse = "|") long_regex <- paste0("(", long_regex, ")")
R内置的gsub函数无法处理这类长度过大的正则模式,执行如下测试代码:
text <- "HPPIZ9166O BHVOF0473O LCVDO3833Z" gsub(long_regex, "marker \\1;", text)
会抛出以下报错:
Error in gsub(long_regex, "marker \1;", text) : assertion 'tree->num_tags == num_tags' failed in executing regexp: file 'tre-compile.c', line 634
报错根因
R内置gsub()默认使用TRE正则引擎,该引擎对正则表达式的编译长度、抽象语法树节点数、捕获组数量存在硬上限。将上千个固定字符串用|拼接为单条正则的写法,会直接触发TRE编译阶段的内部断言错误,该问题属于引擎本身的设计限制,和代码语法无关。
可行解决方法
- 方法1:更换为
stringi包的ICU正则引擎stringi底层依赖的ICU正则引擎没有TRE的长度/节点数限制,可以稳定处理长正则模式。注意ICU引擎的反向引用语法为$1,和TRE的\1语法有区别,示例代码:# 首次使用需安装 # install.packages("stringi") library(stringi) stri_replace_all_regex( str = text, pattern = long_regex, replacement = "marker $1;" ) - 方法2:放弃正则逻辑,改用固定字符串批量匹配(性能最优)
该场景下所有待匹配项都是固定字符串,|拼接正则的本质就是做固定词匹配,完全不需要走正则编译流程。直接调用固定匹配接口批量替换,运行速度比长正则高1~2个数量级,也完全不会触发引擎限制:match_terms <- myFun(1000) stri_replace_all_fixed( str = text, pattern = match_terms, replacement = paste0("marker ", match_terms, ";"), vectorize_all = FALSE ) - 方法3:调用PCRE正则后端
给原生gsub添加perl = TRUE参数,切换到PCRE正则引擎,该引擎对长正则的容忍度远高于默认TRE引擎,无需安装第三方包即可解决报错:
注意:如果正则长度继续增加到上万条词条拼接,PCRE依然可能出现性能骤降、栈溢出问题,优先选择前两种方法。gsub(long_regex, "marker \\1;", text, perl = TRUE)
内容的提问来源于stack exchange,提问作者Fabio Correa
相关产品推荐
相关产品推荐

