【问题标题】:Regex to match all instances not inside quotes正则表达式匹配不在引号内的所有实例
【发布时间】:2011-09-21 16:45:56
【问题描述】:

this q/a,我推断在引号内匹配给定正则表达式 not 的所有实例是不可能的。也就是说,它不能匹配转义引号(例如:"this whole \"match\" should be taken")。如果有一种我不知道的方法可以解决我的问题。

但是,如果没有,我想知道是否有任何可以在 JavaScript 中使用的有效替代方案。我考虑了一下,但无法提供适用于大多数(如果不是全部)情况的优雅解决方案。

具体来说,我只需要使用 .split() 和 .replace() 方法的替代方法,但如果它可以更通用,那将是最好的。

例如:
输入字符串:
+bar+baz"not+or\"+or+\"this+"foo+bar+
将 + 替换为 #,不在引号内,将返回:
#bar#baz"not+or\"+or+\"this+"foo#bar#

【问题讨论】:

    标签: javascript regex escaping quotes


    【解决方案1】:

    实际上,您可以匹配不在任何字符串引号内的正则表达式的所有实例,其中每个开始引号都会再次关闭。比如说,就像你上面的例子,你想匹配\+

    这里的关键观察是,如果一个单词后面有偶数个引号,那么它就在引号之外。这可以建模为前瞻断言:

    \+(?=([^"]*"[^"]*")*[^"]*$)
    

    现在,您不想计算转义引号。这变得有点复杂。而不是 [^"]* ,它前进到下一个引号,您还需要考虑反斜杠并使用 [^"\\]*。到达反斜杠或引号后,如果遇到反斜杠,则需要忽略下一个字符,否则前进到下一个未转义的引号。看起来像(\\.|"([^"\\]*\\.)*[^"\\]*")。结合起来,你到达​​p>

    \+(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)
    

    我承认这是一个神秘。 =)

    【讨论】:

    • 谢谢!没想到这是可能的。我理解了 100% 的理论,大约 60% 的正则表达式,而当我自己编写它时,我理解为 0%。哦,好吧,也许是这些日子之一。
    • 嘿,有没有办法让正则表达式与 JavaScript 的 .split() 方法一起工作?它似乎忽略了全局标志...
    • 没关系,只是忘了把?:放在所有括号内:\+(?=(?:[^"\\]*(?:\\.|"(?:[^"\\]*\\.)*[^"\\]*"))*[^"]*$)
    • 尝试在项目中使用它,但失败了。我发现原因是如果你在两个单引号内有一个双引号 '"'...这将导致字符串中双引号的数量为 odd
    • 请大家看看@zx81在他的回答中提出的解决方案。如果可以使用,那将更容易编写并且具有更好的性能。
    【解决方案2】:

    Azmisov,重新提出这个问题,因为你说你在寻找 any efficient alternative that could be used in JavaScriptany elegant solutions that would work in most, if not all, cases

    碰巧有一个未提及的简单通用解决方案。

    与替代方案相比,此解决方案的正则表达式非常简单:

    "[^"]+"|(\+)
    

    我们的想法是我们匹配但忽略引号内的任何内容以中和该内容(在交替的左侧)。在右侧,我们捕获了所有未被中和到第 1 组的+,替换函数检查第 1 组。下面是完整的工作代码:

    <script>
    var subject = '+bar+baz"not+these+"foo+bar+';
    var regex = /"[^"]+"|(\+)/g;
    replaced = subject.replace(regex, function(m, group1) {
        if (!group1) return m;
        else return "#";
    });
    document.write(replaced);
    

    Online demo

    您可以使用相同的原则来匹配或拆分。请参阅参考资料中的问题和文章,其中还将为您指出代码示例。

    希望这能让您对一个非常通用的方法有不同的想法。 :)

    空字符串呢?

    以上是展示该技术的一般答案。它可以根据您的确切需求进行调整。如果您担心您的文本可能包含空字符串,只需将字符串捕获表达式中的量词从 + 更改为 *

    "[^"]*"|(\+)
    

    demo

    转义引号呢?

    同样,以上是展示该技术的一般答案。 "ignore this match" 正则表达式不仅可以根据您的需要进行细化,您还可以添加多个表达式来忽略。例如,如果您想确保转义引号被充分忽略,您可以首先在另外两个前面添加一个替换 \\"| 以匹配(并忽略)散乱的转义双引号。

    接下来,在捕获双引号字符串内容的"[^"]*" 部分中,您可以添加一个替代项,以确保在它们的" 有机会变成关闭标记之前匹配转义的双引号,将其变为进入"(?:\\"|[^"])*"

    生成的表达式有三个分支:

    1. \\" 匹配并忽略
    2. "(?:\\"|[^"])*" 匹配并忽略
    3. (\+) 匹配,捕获并处理

    请注意,在其他 regex 风格中,我们可以通过后向更轻松地完成这项工作,但 JS 不支持它。

    完整的正则表达式变为:

    \\"|"(?:\\"|[^"])*"|(\+)
    

    参见regex demofull script

    参考

    1. How to match pattern except in situations s1, s2, s3
    2. How to match a pattern unless...

    【讨论】:

    • 这种方法实际上比@Jens 建议的前瞻方法更好。它更容易编写并且具有更好的性能。我没有注意到并使用了前瞻方式,直到我遇到了匹配 1.5M 文本的性能问题,前瞻方式使用了大约 90 秒,而这种方法只需要 600 毫秒。
    • 是的,这样更好 =)
    • 我发现这仅在将示例的第 5 行更改为 if (group1 === undefined ) return m; 时才有效。值得注意的是,我正在寻找空间;不是加号。
    • 如何避免使用这个转义引号?这种模式甚至可能吗?
    • @BrianLow 你是对的。答案旨在以最简单的方式演示该技术。我已根据您的评论对其进行了扩展(请参阅“空字符串怎么办?”和“转义引号怎么办?”部分。
    【解决方案3】:

    您可以分三步完成。

    1. 使用正则表达式全局替换将所有字符串正文内容提取到边表中。
    2. 进行逗号翻译
    3. 使用正则表达式全局替换将字符串主体交换回来

    代码如下

    // Step 1
    var sideTable = [];
    myString = myString.replace(
        /"(?:[^"\\]|\\.)*"/g,
        function (_) {
          var index = sideTable.length;
          sideTable[index] = _;
          return '"' + index + '"';
        });
    // Step 2, replace commas with newlines
    myString = myString.replace(/,/g, "\n");
    // Step 3, swap the string bodies back
    myString = myString.replace(/"(\d+)"/g,
        function (_, index) {
          return sideTable[index];
        });
    

    如果你在设置后运行它

    myString = '{:a "ab,cd, efg", :b "ab,def, egf,", :c "Conjecture"}';
    

    你应该得到

    {:a "ab,cd, efg"
     :b "ab,def, egf,"
     :c "Conjecture"}
    

    它有效,因为在第 1 步之后,

    myString = '{:a "0", :b "1", :c "2"}'
    sideTable = ["ab,cd, efg", "ab,def, egf,", "Conjecture"];
    

    所以 myString 中唯一的逗号是外部字符串。第 2 步,然后将逗号变成换行符:

    myString = '{:a "0"\n :b "1"\n :c "2"}'
    

    最后我们将只包含数字的字符串替换为其原始内容。

    【讨论】:

    • +1 用于优雅的非正则表达式解决方案。不过,对于我正在做的事情,正则表达式更加灵活。
    【解决方案4】:

    虽然 zx81 的答案似乎是性能最好且最干净的答案,但它需要这些修复才能正确捕获转义的引号:

    var subject = '+bar+baz"not+or\\"+or+\\"this+"foo+bar+';
    

    var regex = /"(?:[^"\\]|\\.)*"|(\+)/g;
    

    还有已经提到的“group1 === undefined”或“!group1”。 尤其是 2. 将原始问题中提出的所有问题都考虑在内似乎很重要。

    应该提到的是,这种方法隐含地要求字符串在未转义的引号对之外没有转义的引号。

    【讨论】:

      猜你喜欢
      • 2011-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多