如何将前瞻性正则表达式拆分为 2 个普通正则表达式?
How to split look-ahead regex into 2 plain regexes?
我有一个前瞻性正则表达式 [^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])
。在 my test 中,它从 @@||imasdk.googleapis.com/js/core/bridge*.html
:
中提取 4 个子字符串
|imasdk
.googleapis
.com
/core
我需要用 2 个很好的旧正则表达式重写它,因为我不能使用前瞻(正则表达式引擎不支持)。我将其拆分为 [^a-z0-9%*][a-z0-9%]{3,}
和 [^a-z0-9%*]
,后者在匹配后的子字符串中检查每个第一个正则表达式匹配。
由于某些原因 it extracts /bridge
,因为 .
未在 [^a-z0-9%*]
中列出,而是在 /bridge
之后找到。那么前瞻是如何工作的:它必须是完全匹配、substr(find
结果)还是其他任何东西?在这种情况下,这是否意味着每个结尾字符都不应来自集合 a-z0-9%*
?
在 Rust 中,代码如下所示:
lazy_static! {
// WARNING: the original regex is `"[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])"` but Rust's regex
// does not support look-around, so we have to check it programmatically for the last match
static ref REGEX: Regex = Regex::new(r###"[^a-z0-9%*][a-z0-9%]{3,}"###).unwrap();
static ref LOOKAHEAD_REGEX: Regex = Regex::new(r###"[^a-z0-9%*]"###).unwrap();
}
let pattern_lowercase = pattern.to_lowercase();
let results = REGEX.find_iter(&pattern_lowercase);
for (is_last, each_candidate) in results.identify_last() {
let mut candidate = each_candidate.as_str();
if !is_last {
// have to simulate positive-ahead check programmatically
let ending = &pattern_lowercase[each_candidate.end()..]; // substr after the match
println!("searching in {:?}", ending);
let lookahead_match = LOOKAHEAD_REGEX.find(ending);
if lookahead_match.is_none() {
// did not find anything => look-ahead is NOT positive
println!("NO look-ahead match!");
break;
} else {
println!("found look-ahead match: {:?}", lookahead_match.unwrap().as_str());
}
}
...
测试输出:
"|imasdk":
searching in ".googleapis.com/js/core/bridge*.html"
found look-ahead match: "."
".googleapis":
searching in ".com/js/core/bridge*.html"
found look-ahead match: "."
".com":
searching in "/js/core/bridge*.html"
found look-ahead match: "/"
"/core":
searching in "/bridge*.html"
found look-ahead match: "/"
"/bridge":
searching in "*.html"
found look-ahead match: "."
^ 在这里你可以看到 /bridge
是由于跟随 .
和 它是不正确的。
您的 LOOKAHEAD_REGEX
在匹配后的任何位置查找不在范围内的字符,但具有前瞻性的原始正则表达式仅在匹配后立即查看单个字符。这就是为什么您的代码找到 /bridge
而 regex101 没有找到的原因:您的代码在匹配后的某处看到 .
而 regex101 只看到 *
.
您可以通过锚定 LOOKAHEAD_REGEX
来修复您的代码,使其只查看第一个字符:^[^a-z0-9%*]
.
或者,按照@Sven Marnach 的建议,您可以使用单个正则表达式匹配完整表达式:[^a-z0-9%*][a-z0-9%]{3,}[^a-z0-9%*]
,并去掉匹配项的最后一个字符。
我有一个前瞻性正则表达式 [^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])
。在 my test 中,它从 @@||imasdk.googleapis.com/js/core/bridge*.html
:
|imasdk
.googleapis
.com
/core
我需要用 2 个很好的旧正则表达式重写它,因为我不能使用前瞻(正则表达式引擎不支持)。我将其拆分为 [^a-z0-9%*][a-z0-9%]{3,}
和 [^a-z0-9%*]
,后者在匹配后的子字符串中检查每个第一个正则表达式匹配。
由于某些原因 it extracts /bridge
,因为 .
未在 [^a-z0-9%*]
中列出,而是在 /bridge
之后找到。那么前瞻是如何工作的:它必须是完全匹配、substr(find
结果)还是其他任何东西?在这种情况下,这是否意味着每个结尾字符都不应来自集合 a-z0-9%*
?
在 Rust 中,代码如下所示:
lazy_static! {
// WARNING: the original regex is `"[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])"` but Rust's regex
// does not support look-around, so we have to check it programmatically for the last match
static ref REGEX: Regex = Regex::new(r###"[^a-z0-9%*][a-z0-9%]{3,}"###).unwrap();
static ref LOOKAHEAD_REGEX: Regex = Regex::new(r###"[^a-z0-9%*]"###).unwrap();
}
let pattern_lowercase = pattern.to_lowercase();
let results = REGEX.find_iter(&pattern_lowercase);
for (is_last, each_candidate) in results.identify_last() {
let mut candidate = each_candidate.as_str();
if !is_last {
// have to simulate positive-ahead check programmatically
let ending = &pattern_lowercase[each_candidate.end()..]; // substr after the match
println!("searching in {:?}", ending);
let lookahead_match = LOOKAHEAD_REGEX.find(ending);
if lookahead_match.is_none() {
// did not find anything => look-ahead is NOT positive
println!("NO look-ahead match!");
break;
} else {
println!("found look-ahead match: {:?}", lookahead_match.unwrap().as_str());
}
}
...
测试输出:
"|imasdk":
searching in ".googleapis.com/js/core/bridge*.html"
found look-ahead match: "."
".googleapis":
searching in ".com/js/core/bridge*.html"
found look-ahead match: "."
".com":
searching in "/js/core/bridge*.html"
found look-ahead match: "/"
"/core":
searching in "/bridge*.html"
found look-ahead match: "/"
"/bridge":
searching in "*.html"
found look-ahead match: "."
^ 在这里你可以看到 /bridge
是由于跟随 .
和 它是不正确的。
您的 LOOKAHEAD_REGEX
在匹配后的任何位置查找不在范围内的字符,但具有前瞻性的原始正则表达式仅在匹配后立即查看单个字符。这就是为什么您的代码找到 /bridge
而 regex101 没有找到的原因:您的代码在匹配后的某处看到 .
而 regex101 只看到 *
.
您可以通过锚定 LOOKAHEAD_REGEX
来修复您的代码,使其只查看第一个字符:^[^a-z0-9%*]
.
或者,按照@Sven Marnach 的建议,您可以使用单个正则表达式匹配完整表达式:[^a-z0-9%*][a-z0-9%]{3,}[^a-z0-9%*]
,并去掉匹配项的最后一个字符。