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

Azm*_*sov 56 javascript regex quotes escaping

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

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

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

例如:
输入字符串:
+bar+baz"not+or\"+or+\"this+"foo+bar+
replace + with#,而不是引号内,将返回:
#bar#baz"not+or\"+or+\"this+"foo#bar#

Jen*_*ens 96

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

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

\+(?=([^"]*"[^"]*")*[^"]*$)
Run Code Online (Sandbox Code Playgroud)

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

\+(?=([^"\\]*(\\.|"([^"\\]*\\.)*[^"\\]*"))*[^"]*$)
Run Code Online (Sandbox Code Playgroud)

我承认这有点神秘.=)

  • 请大家在答案中查看@ zx81建议的解决方案.如果可以使用的话,它更容易编写并具有更好的性能. (5认同)
  • 谢谢!没想到有可能.我理解100%的理论,大约60%的正则表达式,当我自己编写它时,我降到0%.哦,好吧,也许是其中的一天. (3认同)
  • 在项目中尝试使用它并且失败了.我发现原因是如果你在两个单引号内有一个双引号''''`这会导致字符串中双引号的数量为'奇数' (3认同)

zx8*_*x81 52

阿兹米索夫,复活这个问题,因为你说你在寻找any efficient alternative that could be used in JavaScriptany elegant solutions that would work in most, if not all, cases.

碰巧有一个没有提及的简单,通用的解决方案.

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

"[^"]+"|(\+)
Run Code Online (Sandbox Code Playgroud)

我们的想法是匹配但忽略引号内的任何内容来中和该内容(在交替的左侧).在右侧,我们捕获+未被中和到第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);
Run Code Online (Sandbox Code Playgroud)

在线演示

您可以使用相同的原则进行匹配或拆分.请参阅参考中的问题和文章,该文章还将指出代码示例.

希望这能为您提供一种非常通用的方法.:)

空字符串怎么样?

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

"[^"]*"|(\+)
Run Code Online (Sandbox Code Playgroud)

演示.

Escaped Quotes怎么样?

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

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

结果表达式有三个分支:

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

请注意,在其他正则表达式中,我们可以使用lookbehind更轻松地完成这项工作,但JS不支持它.

完整的正则表达式成为:

\\"|"(?:\\"|[^"])*"|(\+)
Run Code Online (Sandbox Code Playgroud)

请参阅regex演示完整脚本.

参考

  1. 如何匹配模式除了情况s1,s2,s3
  2. 除非......如何匹配模式

  • 这种方法实际上比@Jens建议的前瞻方式更好.它更容易编写并具有更好的性能.我没有注意到并使用了前瞻方式,直到我遇到一个与1.5M文本匹配的性能问题时,使用了大约90秒的预测方式,而这种方法只需要600ms. (5认同)
  • 这不是匹配双引号内的所有字符吗?我认为问题是如何在引号之外匹配 (4认同)
  • 您将如何使用它来避免转义引号?这种模式有可能吗? (2认同)

Mik*_*uel 6

您可以分三步完成.

  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];
    });
Run Code Online (Sandbox Code Playgroud)

如果你在设置后运行它

myString = '{:a "ab,cd, efg", :b "ab,def, egf,", :c "Conjecture"}';
Run Code Online (Sandbox Code Playgroud)

你应该得到

{:a "ab,cd, efg"
 :b "ab,def, egf,"
 :c "Conjecture"}
Run Code Online (Sandbox Code Playgroud)

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

myString = '{:a "0", :b "1", :c "2"}'
sideTable = ["ab,cd, efg", "ab,def, egf,", "Conjecture"];
Run Code Online (Sandbox Code Playgroud)

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

myString = '{:a "0"\n :b "1"\n :c "2"}'
Run Code Online (Sandbox Code Playgroud)

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