【问题标题】:Javascript Lookbehind with Global Search Overlapped全局搜索重叠的 Javascript Lookbehind
【发布时间】:2019-04-02 21:31:54
【问题描述】:

对于 Javascript 中的后向正则表达式有几种(有时很棘手)解决方案。但是,如果我需要 零宽度! 使用全局搜索查看表达式,这是最简单的方法,这可能会重叠。例如。使用/(?<=[01])\d/g,我们可以执行以下操作:

let a = "--1--01001--1087---";
let result = a.replace(/(?<=[01])\d/g, "#");
// This should print "--1--0####--1##7---" if lookbehind would be supported
console.log(result);

或者另一个例子:我们如何创建一个类似\b 的表达式,它只适用于字母 ([a-zA-Z])。 (前瞻是没有问题的。只是JS不支持后瞻)。

【问题讨论】:

    标签: javascript regex global lookbehind


    【解决方案1】:

    通过反转进行回顾方法在这里似乎是最简单的。 这种方法最适合像这样的短数字模式。

    function revStr(str) {
        return str.split('').reverse().join('');
    }
    
    var s = "--1--01001--1087---";
    var rxp = /\d(?=[01])/g;
    
    var result = revStr(revStr(s).replace(rxp, "#"));
    document.write(result);

    逻辑:

    • \d(?=[01])(?&lt;=[01])\d 的反向正则表达式
    • 我们用revStr(s)函数反转输入字符串
    • 我们再次反转替换结果以获得最终结果。

    注意

    如果您在 JavaScript 中同时需要可变宽度后视前瞻,我建议您阅读 Steven Levithan 的 JavaScript Regex Lookbehind Redux 文章,您可以在其中找到一个示例函数,展示如何使用XRegExp 实现该行为。以下是函数:

    // Simulating infinite-length leading lookbehind in JavaScript. Uses XRegExp.
    // Captures within lookbehind are not included in match results. Lazy
    // repetition in lookbehind may lead to unexpected results.
    
    (function (XRegExp) {
    
        function prepareLb(lb) {
            // Allow mode modifier before lookbehind
            var parts = /^((?:\(\?[\w$]+\))?)\(\?<([=!])([\s\S]*)\)$/.exec(lb);
            return {
                // $(?!\s) allows use of (?m) in lookbehind
                lb: XRegExp(parts ? parts[1] + "(?:" + parts[3] + ")$(?!\\s)" : lb),
                // Positive or negative lookbehind. Use positive if no lookbehind group
                type: parts ? parts[2] === "=" : !parts
            };
        }
    
        XRegExp.execLb = function (str, lb, regex) {
            var pos = 0, match, leftContext;
            lb = prepareLb(lb);
            while (match = XRegExp.exec(str, regex, pos)) {
                leftContext = str.slice(0, match.index);
                if (lb.type === lb.lb.test(leftContext)) {
                    return match;
                }
                pos = match.index + 1;
            }
            return null;
        };
    
        XRegExp.testLb = function (str, lb, regex) {
            return !!XRegExp.execLb(str, lb, regex);
        };
    
        XRegExp.searchLb = function (str, lb, regex) {
            var match = XRegExp.execLb(str, lb, regex);
            return match ? match.index : -1;
        };
    
        XRegExp.matchAllLb = function (str, lb, regex) {
            var matches = [], pos = 0, match, leftContext;
            lb = prepareLb(lb);
            while (match = XRegExp.exec(str, regex, pos)) {
                leftContext = str.slice(0, match.index);
                if (lb.type === lb.lb.test(leftContext)) {
                    matches.push(match[0]);
                    pos = match.index + (match[0].length || 1);
                } else {
                    pos = match.index + 1;
                }
            }
            return matches;
        };
    
        XRegExp.replaceLb = function (str, lb, regex, replacement) {
            var output = "", pos = 0, lastEnd = 0, match, leftContext;
            lb = prepareLb(lb);
            while (match = XRegExp.exec(str, regex, pos)) {
                leftContext = str.slice(0, match.index);
                if (lb.type === lb.lb.test(leftContext)) {
                    // Doesn't work correctly if lookahead in regex looks outside of the match
                    output += str.slice(lastEnd, match.index) + XRegExp.replace(match[0], regex, replacement);
                    lastEnd = match.index + match[0].length;
                    if (!regex.global) {
                        break;
                    }
                    pos = match.index + (match[0].length || 1);
                } else {
                    pos = match.index + 1;
                }
            }
            return output + str.slice(lastEnd);
        };
    
    }(XRegExp));
    

    每个函数都接受三个参数:要搜索的字符串、作为字符串的后向模式(可以使用 XRegExp 语法扩展)和主正则表达式。 XRegExp.replaceLb 接受第四个参数作为替换值,可以是字符串或函数。

    用法示例如下:

    XRegExp.execLb("Fluffy cat", "(?i)(?<=fluffy\\W+)", XRegExp("(?i)(?<first>c)at"));
    // -> ["cat", "c"]
    // Result has named backref: result.first -> "c"
    
    XRegExp.execLb("Fluffy cat", "(?i)(?<!fluffy\\W+)", /cat/i);
    // -> null
    
    XRegExp.testLb("Fluffy cat", "(?i)(?<=fluffy\\W+)", /cat/i);
    // -> true
    
    XRegExp.testLb("Fluffy cat", "(?i)(?<!fluffy\\W+)", /cat/i);
    // -> false
    
    XRegExp.searchLb("Catwoman's fluffy cat", "(?i)(?<=fluffy\\W+)", /cat/i);
    // -> 18
    
    XRegExp.searchLb("Catwoman's fluffy cat", "(?i)(?<!fluffy\\W+)", /cat/i);
    // -> 0
    
    XRegExp.matchAllLb("Catwoman's cats are fluffy cats", "(?i)(?<=fluffy\\W+)", /cat\w*/i);
    // -> ["cats"]
    
    XRegExp.matchAllLb("Catwoman's cats are fluffy cats", "(?i)(?<!fluffy\\W+)", /cat\w*/i);
    // -> ["Catwoman", "cats"]
    
    XRegExp.replaceLb("Catwoman's fluffy cat is a cat", "(?i)(?<=fluffy\\W+)", /cat/ig, "dog");
    // -> "Catwoman's fluffy dog is a cat"
    
    XRegExp.replaceLb("Catwoman's fluffy cat is a cat", "(?i)(?<!fluffy\\W+)", /cat/ig, "dog");
    // -> "dogwoman's fluffy cat is a dog"
    
    XRegExp.replaceLb("Catwoman's fluffy cat is a cat", "(?i)(?<!fluffy\\W+)", /cat/ig, function ($0) {
        var first = $0.charAt(0);
        return first === first.toUpperCase() ? "Dog" : "dog";
    });
    // -> "Dogwoman's fluffy cat is a dog"
    

    【讨论】:

    • 除非您需要可变宽度的后视和可变宽度的前瞻,否则这种反转方法似乎最适合重叠匹配。而且代码量也不是很大。此外,正则表达式模式并不难逆转。
    • 我发现这说的是,但看起来,别无选择。实际上,有时我们还需要look-behind 和look-ahead(不常见:我想,我在Perl 16 年中使用了大约5 次,但仍然如此)。
    • 如果您需要 JavaScript 中的可变宽度后视和前瞻,我可以推荐阅读 Steven Levithan 的 JavaScript Regex Lookbehind Redux 文章,您可以在其中找到一个示例函数,展示如何实现它使用XRegExp 的行为。
    猜你喜欢
    • 2012-05-01
    • 2011-12-18
    • 2023-03-19
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多