【问题标题】:Regexp group not excluding dots不排除点的正则表达式组
【发布时间】:2019-03-13 16:04:32
【问题描述】:

假设我有以下字符串:div.classOneA.classOneB#idOne

尝试编写一个从中提取类(classOneA,classOneB)的正则表达式。我能够做到这一点,但只有Lookbehind assertion

看起来像这样:

'div.classOneA.classOneB#idOne'.match(/(?<=\.)([^.#]+)/g)
> (2) ["classOneA", "classOneB"]

现在我想在不使用后向方法的情况下将其存档,并且不明白为什么我的解决方案不起作用。

'div.classOneA.classOneB#idOne'.match(/\.([^.#]+)/g)
> (2) [".classOneA", ".classOneB"]

认为分组将解决我的问题,但所有匹配项也包含点。

【问题讨论】:

    标签: javascript regex


    【解决方案1】:

    在 Javascript 中没有一种既可以匹配多次(/g 选项)又可以获取捕获组(在括号中)的好方法。试试这个:

    var input = "div.classOneA.classOneB#idOne";
    var regex = /\.([^.#]+)/g;
    
    var matches, output = [];
    while (matches = regex.exec(input)) {
        output.push(matches[1]);
    }
    

    【讨论】:

    • 你就在我前面 :-)
    【解决方案2】:

    这是因为使用 g 修饰符可以获得所有匹配的子字符串,但不能获得其匹配的组(这就像 (...) 对的工作方式与 (?:...) 的一对一样。

    你看。没有g修饰符:

    > 'div.classOneA.classOneB#idOne'.match(/\.([^.#]+)/)
    [ '.classOneA',
      'classOneA',
      index: 3,
      input: 'div.classOneA.classOneB#idOne',
      groups: undefined ]
    

    使用g 修饰符:

    > 'div.classOneA.classOneB#idOne'.match(/\.([^.#]+)/g)
    [ '.classOneA', '.classOneB' ]
    

    换句话说:您获得所有匹配项,但每个匹配项仅获得整个匹配项(0 项)。

    有很多解决方案:

    1. 使用 LookBehind 断言,就像您自己指出的那样。

    2. 稍后修复每个结果添加.map(x=&gt;x.replace(/^\./, ""))

    3. 或者,如果您的输入结构不会比您提供的示例复杂得多,只需使用更便宜的方法:

      > 'div.classOneA.classOneB#idOne'.replace(/#.*/, "").split(".").slice(1)
      [ 'classOneA', 'classOneB' ]
      
    4. 使用.replace() + 回调而不是.match() 以便能够访问每场比赛的捕获组:

      const str = 'div.classOneA.classOneB#idOne';
      const matches = [];
      str.replace(/\.([^.#]+)/g, (...args)=>matches.push(args[1]))
      console.log(matches); // [ 'classOneA', 'classOneB' ]
      

    我会推荐第三个(如果没有其他可能的输入最终会破坏它),因为它更有效(实际的正则表达式只使用一次来修剪 '#idOne' 部分)。

    【讨论】:

    • 哇,这第四个解决方案相当棘手。没想到用这种方式替换。感谢您的努力,即使我已经接受了答案。
    • 我这样做只是为了好玩 :-) 尽管您的问题似乎是过早的优化,但也确实有充分的理由避免使用后向方法:首先是因为 lookarround操作是正则表达式实现中最昂贵的操作之一,因此在过于频繁执行的代码中,最好避免它们。
    • ...另一方面,正因为如此,它们在 javascript 中一直不可用,直到(如果我没记错的话)ES6,所以如果你的代码需要在旧的 javascript 引擎中运行,lookarround 方法不是一个可行的选择。
    • 我没有时间去做,但是 replace 方法可以泛化实现一个 mathcString() 函数甚至重载 String .match() 方法(我不建议这样做,因为它是一种反模式)但它可能是一个有趣的练习……
    【解决方案3】:

    如果你想扩展你的正则表达式。您可以在结果上简单地map 并将. 替换为空字符串

    let op = 'div.classOneA.classOneB#idOne'.match(/\.([^.#]+)/g)
             .map(e=> e.replace(/\./g,''))
    
    console.log(op)

    【讨论】:

      【解决方案4】:

      如果您知道要搜索包含class 的文本,则可以使用类似

      'div.classOneA.classOneB#idOne'.match(/class[^.#]+/g)
      

      如果你只知道文本前面有一个点,那么你必须使用lookbehind。

      【讨论】:

        【解决方案5】:

        这个正则表达式可以在没有后向断言的情况下工作:

        'div.classOneA.classOneB#idOne'.match(/\.[^\.#]+/g).map(item => item.substring(1));
        

        最近在 JavaScript 中不提供 Lookbehind 断言。

        【讨论】:

          【解决方案6】:

          我不是使用正则表达式的专家——尤其是在 Javascript 中——但在对 MDN 进行了一些研究之后,我发现了您的尝试不起作用的原因以及如何解决。

          问题在于将.match 与带有/g 标志will ignore capturing groups 的正则表达式一起使用。因此,您必须在 regexp 对象上使用.exec 方法,使用循环多次执行它以获得所有结果。

          所以下面的代码是有效的,并且可以适应类似的情况。 (注意grp[1] - 这是因为.exec 返回的数组的第一个元素是整个匹配,组是后续元素。)

          var regExp = /\.([^.#]+)/g
          var result = [];
          var grp;
          while ((grp = regExp.exec('div.classOneA.classOneB#idOne')) !== null) {
            result.push(grp[1]);
          }
          console.log(result)

          【讨论】:

            猜你喜欢
            • 2022-11-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多