Javascript 正则表达式 - 同时查看和展望

Javascript regexes - Lookbehind and lookahead at the same time

本文关键字:正则表达式 Javascript      更新时间:2023-09-26

我正在尝试在 JavaScript 中创建一个与字符b匹配的正则表达式,如果它之前或后面没有字符a

显然,JavaScript正则表达式没有容易实现的负面回溯,这使得任务变得困难。我想出了以下方法,但它不起作用。

"ddabdd".replace(new RegExp('(?:(?![a]b(?![a])))*b(?![a])', 'i'),"c");

是我能想到的最好的。在这里,b不应该匹配,因为它前面有a,但它匹配。

所以一些关于我想要实现的例子

"ddbdd" matches the b
"b" matches the b
"ddb" matches the b
"bdd" matches the b
"ddabdd" or "ddbadd" does not match the b

似乎您可以使用包含字符串锚点开头或"b"前面的否定字符类的捕获组,同时使用负前瞻来断言"a"不跟随。然后,您只需在替换调用中引用$1以及替换字符串的其余部分。

var s = 'ddbdd b ddb bdd ddabdd ddabdd ddbadd';
var r = s.replace(/(^|[^a])b(?!a)/gi, '$1c');
console.log(r); //=> "ddcdd c ddc cdd ddabdd ddabdd ddbadd"

编辑:正如@nhahtdh指出的关于连续字符的评论,您可以考虑回调。

var s = 'ddbdd b ddb bdd ddabdd ddabdd ddbadd sdfbbfds';
var r = s.replace(/(a)?b(?!a)/gi, function($0, $1) {
    return $1 ? $0 : 'c';
});
console.log(r); //=> "ddcdd c ddc cdd ddabdd ddabdd ddbadd sdfccfds"
在这种情况下,无法

仅使用正则表达式模拟后视的行为,因为字符串中可能存在连续b,这需要后视的零宽度属性来检查紧接前面的字符。

由于后视中的条件非常简单,因此您可以在替换功能中检查它:

inputString.replace(/b(?!a)/gi, function ($0, idx, str) {
    if (idx == 0 || !/a/i.test(str[idx - 1])) { // Equivalent to (?<!a)
        return 'c';
    } else {
        return $0; // $0 is the text matched by /b(?!a)/
    }
});

你在这里真正想做的是为一种小语言编写一个解析器。正则表达式擅长一些解析任务,但在许多任务上很糟糕(JS正则表达式有些功能不足(。您可能能够找到在特定情况下工作的正则表达式,然后当您的语法规则更改时,正则表达式可能很难或不可能更改以反映这一点。下面的简单程序的优点是可读且可维护。它完全按照它所说的去做。

function find_bs(str) {
    var indexes = [];
    for (var i = 0; i < str.length; i++) {
        if (str[i] === 'b' && str[i-1] !== 'a' && str[i+1] !== 'a')
            indexes.push(i);
    }
    return indexes;
}

使用正则表达式

如果你绝对坚持使用正则表达式,你可以结合RegExp.exec使用重置正则表达式上的lastIndex属性的技巧:

function find_bs(str) {
    var indexes = [];
    var regexp = /.b[^a]|[^a]b./g;
    var matches;
    while (matches = regexp.exec(str)) {
        indexes.push(matches.index + 1);
        regexp.lastIndex -= 2;
    }
    return indexes;
}

您需要调整逻辑以处理字符串的开头和结尾。

这是如何工作的

我们使用正则表达式找到整个xbx字符串。b的索引将是一加比赛的索引,所以我们记录下来。在我们进行下一次匹配之前,我们重置 lastIndex ,它控制搜索将继续的起点,回到b ,因此它充当任何后续潜在匹配的第一个字符。