提问人:BBB 提问时间:10/8/2023 更新时间:10/8/2023 访问量:68
strsplit 和 stri_extract_all_regex 的贪婪
Greediness of strsplit and stri_extract_all_regex
问:
我正在努力获得并始终如一地工作。按 strsplit 拆分是所需的行为(在实践中,我有一长串由代码动态生成的替代匹配项)。strsplit
stri_extract_all_regex
例如
strsplit("1234567","(23)|(234)")
[[1]]
[1] "1" "567"
与
stri_extract_all_regex("1234567","(23)|(234)")
[[1]]
[1] "23"
提取所需的输出为
[[1]]
[1] "234"
答:
3赞
SamR
10/8/2023
#1
正则表达式引擎:PCRE、ERE 和 ICU
默认情况下,Base R 使用扩展正则表达式 (ERE),或者使用 Perl 兼容正则表达式 (PCRE)(如果指定了 )。相反,文档指出:perl = TRUE
stringi
提供对 ICU 中实现的正则表达式引擎的访问,该引擎的灵感来自 JDK 1.4 中的 Java
util.regex
如为什么顺序在此正则表达式中具有交替性中所述,顺序很重要,因为这是正则表达式引擎将尝试匹配的顺序。这适用于该问题中的.NET正则表达式引擎,我们可以在下面看到它也适用于PCRE和ICU,但不适用于ERE,这就是为什么您得到不同的结果(除非您设置)。strsplit()
perl = TRUE
library(stringi)
x <- "1234567"
pattern <- "(23)|(234)"
# Base R (ERE) engine
regmatches(x, gregexpr(pattern, x)) |> unlist()
# [1] "234"
# Base R (PCRE) engine
regmatches(x, gregexpr(pattern, x, perl = TRUE)) |> unlist()
# [1] "23"
stri_extract_all_regex(x, pattern) |> unlist()
# [1] "23"
正如这个问题的答案所述:
这有点像 ||C# 中的运算符。一旦左侧匹配,右侧将被忽略。
因此,我们需要切换模式,以便首选(即最长)匹配首先:.pattern <- "(234)|(23)"
排列您的模式,以便最长的匹配是第一个
由于您的模式是动态生成的,因此您不能只对订单进行硬编码。但是,您可以编写一个函数,先按最长的模式对其进行排序。
switch_pattern_order <- function(pattern){
pattern_split <- unlist(strsplit(pattern, "|", fixed = TRUE))
pattern_sorted <- pattern_split[order(nchar(pattern_split), decreasing = TRUE)]
new_pattern <- paste(pattern_sorted, collapse = "|")
new_pattern
}
这假设唯一的运算符是,但如果有其他运算符,则可以扩展它。"|"
现在,这将适用于任何正则表达式引擎:
pattern <- switch_pattern_order(pattern) # "(234)|(23)"
regmatches(x, gregexpr(pattern, x)) |> unlist()
# [1] "234"
regmatches(x, gregexpr(pattern, x, perl = TRUE)) |> unlist()
# [1] "234"
stri_extract_all_regex(x, pattern) |> unlist()
# [1] "234"
评论
0赞
BBB
10/8/2023
谢谢你的细节。这就是我在等待答案时选择做的事情(nchar 排序)。但是,我觉得这不是最通用的解决方案,因为它仅适用于替代的“固定”字符串(正如我在我的应用程序中一样),但不适用于更通用的正则表达式字符串,所以我希望有人也能有一个更通用的解决方案。我认为一般的解决方案是选择(而不是)?regmatches
perl
0赞
BBB
10/8/2023
我还使用区分大小写的“标志”和.有没有一种简单的方法可以找出哪些引擎支持这一点?(?i)
(?-i)
0赞
SamR
10/8/2023
@BBB 是的,在这种情况下,我认为解决方案是使用 ERE 引擎。它似乎支持区分大小写标志作为返回。你也可以做.regmatches("AABB", gregexpr("(?i)aa", "AABB"))
"AA"
gregexpr(pattern, x, ignore.case = TRUE)
1赞
BBB
10/9/2023
超。谢谢。我当然知道ignore.case,但这种方式允许组合敏感和不敏感的正则表达式部分。
评论
strsplit()
perl = TRUE
stringi
ICU
stringi
stringi
strsplit
regmatches(x, gregexpr(pattern, x))
234
strsplit()
stri_extract_all_regex()