正则表达式匹配所有不在引号内的实例

IT技术 javascript regex escaping quotes
2021-01-25 21:32:57

这个 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个回答

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

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

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

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

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

我承认这有点神秘。=)

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

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);

在线演示

您可以使用相同的原理进行匹配或拆分。请参阅参考资料中的问题和文章,其中也将指向您的代码示例。

希望这能让您对执行此操作的非常通用方法有不同的想法。:)

空字符串呢?

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

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

演示

转义行情呢?

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

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

结果表达式具有三个分支:

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

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

完整的正则表达式变为:

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

请参阅正则表达式演示完整脚本

参考

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

您可以分三步完成。

  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 表示优雅的非正则表达式解决方案。不过,正则表达式对我正在做的事情更灵活一些。
2021-04-02 21:32:57

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

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

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

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

不过应该提到的是,此方法隐式要求字符串在未转义的引号对之外没有转义的引号。