正则表达式的替代方法:匹配不在引号内的所有实例

时间:2011-06-24 02:00:18

标签: javascript regex escaping quotes

this q/a开始,我推断出在引号内匹配给定正则表达式的所有实例而不是是不可能的。也就是说,它无法匹配转义引号(例如:"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#

4 个答案:

答案 0 :(得分:90)

实际上,您可以匹配任何字符串中不是引号内的正则表达式的所有实例,其中每个开头引号都会再次关闭。如上例所示,您希望匹配\+

这里的关键观察是,如果在其后面有偶数引号,则单词在引号之外。这可以建模为前瞻性断言:

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

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

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

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

答案 1 :(得分:48)

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

Escaped Quotes怎么样?

同样,以上是展示该技术的一般答案。 “忽略此匹配”正则表达式不仅可以根据您的需要进行优化,还可以添加多个表达式来忽略。例如,如果要确保已完全忽略转义引号,则可以首先在其他两个引号之前添加替换\\"|,以便匹配(并忽略)散乱转义的双引号。

接下来,在捕获双引号字符串内容的"[^"]*"部分中,您可以添加一个替换,以确保在"有机会变成结束之前匹配转义的双引号哨兵,将其变成"(?:\\"|[^"])*"

结果表达式有三个分支:

  1. \\"匹配,忽略
  2. "(?:\\"|[^"])*"匹配,忽略
  3. (\+)匹配,捕获和处理
  4. 请注意,在其他正则表达式中,我们可以使用lookbehind更轻松地完成这项工作,但JS不支持它。

    完整的正则表达式成为:

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

    请参阅regex demofull script

    参考

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

答案 2 :(得分:6)

您可以分三步完成。

  1. 使用正则表达式全局替换将所有字符串正文内容提取到边表中。
  2. 做你的逗号翻译
  3. 使用正则表达式全局替换来交换字符串主体
  4. 以下代码

    // 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"}'
    

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

答案 3 :(得分:1)

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

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

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

同样已经提到的&#34; group1 === undefined&#34;或&#34;!group1&#34;。 特别是2.实际上考虑原始问题中提出的所有问题似乎很重要。

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