regex 如何用正则表达式匹配重叠的字符串?

2g32fytz  于 2023-10-22  发布在  其他
关注(0)|答案(6)|浏览(132)

假设我有绳子

"12345"

如果我.match(/\d{3}/g),我只得到一个匹配,"123"。为什么我不能得到[ "123", "234", "345" ]

jutyujz0

jutyujz01#

带有全局标志regex的string#match返回一个由 * 匹配的子字符串 * 组成的数组。/\d{3}/g正则表达式匹配并 * 消耗 (= 读取缓冲区并将其索引推进到当前匹配字符 * 之后的位置)3位序列。因此,在“吃掉”123之后,索引位于3之后,并且唯一剩下的用于解析的子串是45-这里没有匹配。
我认为regex101.com中使用的技术也值得考虑:使用零宽度Assert(带有捕获组的积极前瞻)来测试输入字符串中的所有位置。在每次测试之后,* RegExp.lastIndex *(它是正则表达式的读/写整数属性,指定下一个匹配开始的索引)被“手动”推进以避免无限循环。
注意,它是一种在.NET(Regex.Matches)、Python(re.findall)、PHP(preg_match_all)、Ruby(String#scan)中实现的技术,也可以在Java中使用。下面是一个使用matchAll的演示:

var re = /(?=(\d{3}))/g;
console.log( Array.from('12345'.matchAll(re), x => x[1]) );

下面是一个兼容ES 5的demo:

var re = /(?=(\d{3}))/g;
var str = '12345';
var m, res = [];
 
while (m = re.exec(str)) {
    if (m.index === re.lastIndex) {
        re.lastIndex++;
    }
    res.push(m[1]);
}

console.log(res);

这是一个regex101.com demo
请注意,同样可以使用“常规”消耗\d{3}模式编写,并在每次成功匹配后手动将re.lastIndex设置为m.index+1值:

var re = /\d{3}/g;
var str = '12345';
var m, res = [];

while (m = re.exec(str)) {
    res.push(m[0]);
    re.lastIndex = m.index + 1; // <- Important
}
console.log(res);
6pp0gazn

6pp0gazn2#

你不能单独使用正则表达式来完成这个任务,但是你可以非常接近:

var pat = /(?=(\d{3}))\d/g;
var results = [];
var match;

while ( (match = pat.exec( '1234567' ) ) != null ) {
  results.push( match[1] );
}

console.log(results);

换句话说,您在lookahead中捕获所有三个数字,然后返回并以正常方式匹配一个字符,只是为了推进匹配位置。不管你如何消费这个角色,.的工作原理与\d相同。如果您真的喜欢冒险,可以只使用lookahead,让JavaScript处理碰撞。
此代码改编自this answer

kmynzznz

kmynzznz3#

当一个表达式匹配时,它通常会 * 消耗 * 它匹配的字符。因此,在表达式匹配123之后,只剩下45,这与模式不匹配。

3gtaxfhh

3gtaxfhh4#

要回答“如何”,您可以手动更改最后一个匹配的索引(需要循环):

var input = '12345',
    re = /\d{3}/g,
    r = [],
    m;
while (m = re.exec(input)) {
    re.lastIndex -= m[0].length - 1;
    r.push(m[0]);
}
r; // ["123", "234", "345"]

下面是一个函数,方便使用:

function matchOverlap(input, re) {
    var r = [], m;
    // Prevent infinite loops
    if (!re.global) re = new RegExp(
        re.source, (re+'').split('/').pop() + 'g'
    );
    while (m = re.exec(input)) {
        re.lastIndex -= m[0].length - 1;
        r.push(m[0]);
    }
    return r;
}

使用示例:

matchOverlap('12345', /\D{3}/)      // []
matchOverlap('12345', /\d{3}/)      // ["123", "234", "345"]
matchOverlap('12345', /\d{3}/g)     // ["123", "234", "345"]
matchOverlap('1234 5678', /\d{3}/)  // ["123", "234", "567", "678"]
matchOverlap('LOLOL', /lol/)        // []
matchOverlap('LOLOL', /lol/i)       // ["LOL", "LOL"]
zbq4xfa0

zbq4xfa05#

我会考虑不使用正则表达式。如果你想分成三个一组,你可以从偏移量开始循环字符串:

let s = "12345"
let m = Array.from(s.slice(2), (_, i) => s.slice(i, i+3))
console.log(m)
ffscu2ro

ffscu2ro6#

使用(?=(\w{3}))
(3是序列中的字母数)

相关问题