带有转义引号的引用字符串的正则表达式

Posted

技术标签:

【中文标题】带有转义引号的引用字符串的正则表达式【英文标题】:Regex for quoted string with escaping quotes 【发布时间】:2010-09-19 23:19:02 【问题描述】:

如何使用正则表达式获取子字符串" It's big \"problem "

s = ' function()  return " It\'s big \"problem  ";  ';     

【问题讨论】:

如何在只包含“Is”的字符串中找到“It's”?我会为你解决这个问题,但我不知道在你使用的语言中适用哪些单引号/转义约定。 重复:php: Regex to ignore escaped quotes within quotes 实际上,查看日期,我发现另一个问题与这个问题重复。无论哪种方式,请务必查看my answer。 @ridgerunner:我投票决定按照你的建议关闭它。确实,其他问题是较新的,但也好得多(主要感谢您的回答)。 【参考方案1】:
/(["\']).*?(?<!\\)(\\\\)*\1/is

应该使用任何带引号的字符串

【讨论】:

很好,但是对于请求来说太灵活了(将匹配单引号......)。并且可以简化为 /".*?(? @PhiLho,仅使用单个 (? @PhiLho 使用此输入进行简化:"Martha's" 将导致此匹配:"Martha',这是不正确的。匹配组,以确定使用哪种类型的报价打开它,这一点很重要。 @Swivel 注意 1:我的答案中有一个双反斜杠,不知何故丢失了第二个(因为 Markdown?)。应该用反引号保护。注 2:Markus 是对的……所以它是有缺陷的。不像我的(流行的)答案...... :-) 注3:我的表达中没有单引号,我没有看到你提到的问题,我无法重现它。 (我说我不处理单引号作为分隔符,因为它不是主题。) @PhiLho Huh... 很奇怪。不知道我第一次是如何误解它的。你完全正确。我不知道我是怎么误会你原来的评论的。【参考方案2】:

必须记住,正则表达式并不是所有字符串的灵丹妙药。有些事情用光标和线性、手动、搜索更简单。 CFL 可以很简单地做到这一点,但 CFL 实现并不多(afaik)。

【讨论】:

确实如此,但是这个问题完全在正则表达式的能力范围内,并且有很多实现。【参考方案3】:
/"(?:[^"\\]|\\.)*"/

在 Regex Coach 和 PCRE Workbench 中工作。

javascript 测试示例:

    var s = ' function() return " Is big \\"problem\\", \\no? "; ';
    var m = s.match(/"(?:[^"\\]|\\.)*"/);
    if (m != null)
        alert(m);

【讨论】:

有道理。纯英语:两个引号围绕零个或多个“任何不是引号或反斜杠的字符”或“反斜杠后跟任何字符”。我不敢相信我没想过要这样做...... 我会自己回答。 =) (?:...) 是被动或非捕获组。这意味着它以后不能被反向引用。 经过大量搜索和测试,这是我发现的这个常见问题的真正唯一解决方案。谢谢! 感谢您。我也想匹配单引号,所以我最终将其调整为:/(["'])(?:[^\1\\]|\\.)*?\1/ 使用var s = ' my \\"new\\" string and \"this should be matched\"';,这种做法会导致意想不到的结果。【参考方案4】:

这个来自许多 Linux 发行版中的 nanorc.sample。用于C风格字符串的语法高亮

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

【讨论】:

使用var s = ' my \\"new\\" string and \"this should be matched\"';,这种做法会导致意想不到的结果。 c.nanorc 是我去的第一个地方。直到双重转义像这样" \"(\\\\.|[^\\\"])*\" " 这适用于 libc 中的 egrep 和 re_comp/re_exec 函数。【参考方案5】:
"(?:\\"|.)*?"

交替使用 \". 传递转义的引号,而惰性量词 *? 确保您不会超出引用字符串的末尾。适用于 .NET Framework RE 类

【讨论】:

但因"\\"而失败 It will fail with var s = ' my \\"new\\" string and \"this should be matched\"'; /"(?:(?:\\"|[^"])*)"/g 这应该可以解决【参考方案6】:

ePharaoh 提供的答案是

/"([^"\\]*(\\.[^"\\]*)*)"/

要将上述内容应用于单引号或双引号字符串,请使用

/"([^"\\]*(\\.[^"\\]*)*)"|\'([^\'\\]*(\\.[^\'\\]*)*)\'/

【讨论】:

这是唯一一个对我有用的集合,它包含一个包含 99 个转义的 1.5 KB 大引号字符串。此页面上的所有其他表达式在我的文本编辑器中都因溢出错误而中断。虽然这里的大多数都在浏览器中工作,但请记住一些事情。小提琴:jsfiddle.net/aow20y0L 请参阅下面@MarcAndrePoulin 的答案以获取解释。【参考方案7】:

如果从头开始搜索,也许这样可以吗?

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

【讨论】:

【参考方案8】:

https://***.com/a/10786066/1794894 的更广泛版本

/"([^"\\]50,(\\.[^"\\]*)*)"|\'[^\'\\]50,(\\.[^\'\\]*)*\'|“[^”\\]50,(\\.[^“\\]*)*”/   

这个版本还包含

    最小报价长度为 50 额外类型的引号(打开并关闭

【讨论】:

【参考方案9】:

在regexpal 搞砸了,最后得到了这个正则表达式:(不要问我它是如何工作的,即使我写了它我也几乎不明白,哈哈)

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

【讨论】:

【参考方案10】:

这里提供的大多数解决方案都使用替代的重复路径,即 (A|B)*。

您可能会在大输入时遇到堆栈溢出,因为某些模式编译器使用递归来实现这一点。

例如 Java:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6337993

类似这样的: "(?:[^"\\]*(?:\\.)?)*",或者 Guy Bedford 提供的那个会减少解析步骤的数量,避免大多数堆栈溢出。

【讨论】:

【参考方案11】:
/"(?:[^"\\]++|\\.)*+"/

直接取自安装了 Perl 5.22.0 的 Linux 系统上的 man perlre。 作为一种优化,这个正则表达式使用 +* 的 'posessive' 形式来防止回溯,因为事先知道没有右引号的字符串在任何情况下都不匹配。

【讨论】:

【参考方案12】:

这个在 PCRE 上完美运行,不适合 ***。

"(.*?[^\\])??((\\\\)+)?+"

解释:

    每个带引号的字符串都以 Char 开头:"; 它可以包含任意数量的任意字符:.*? Lazy match;以非转义字符 [^\\] 结尾; 语句 (2) 是 Lazy(!) 可选的,因为字符串可以为空 ("")。所以:(.*?[^\\])?? 最后,每个带引号的字符串都以 Char(") 结尾,但它前面可以有偶数个转义符号对 (\\\\)+;它是 Greedy(!) 可选的:((\\\\)+)?+ Greedy matching,因为字符串可以为空或没有结尾对!

【讨论】:

这不是世界上最有效的模式,但这个想法很有趣。请注意,您可以像这样缩短它:"(.*?[^\\])?(\\\\)*"【参考方案13】:

这里有一个可以同时使用 " 和 ' 并且您可以轻松地在开始时添加其他人。

("|')(?:\\\1|[^\1])*?\1

它使用反向引用 (\1) 精确匹配第一组中的内容(" 或 ')。

http://www.regular-expressions.info/backref.html

【讨论】:

这是一个很好的解决方案,但是[^\1] 应该替换为.,因为没有反反向引用这样的东西,反正也没关系。在任何不好的事情发生之前,第一个条件总是匹配的。 @SephReed – 用. 替换[^\1] 会有效地将这个正则表达式更改为("|').*?\1,然后它会匹配"foo \" bar" 中的"foo\"。也就是说,让[^\1] 实际工作很难。 @​mathiashansen – 使用笨重且昂贵的 (?!\1). 会更好(所以整个正则表达式,经过一些效率清理,将是 (["'])(?:\\.|(?!\1).)*+\1。如果您的引擎,+ 是可选的不支持。【参考方案14】:

之前没有提到的一个选项是:

    反转字符串。 对反向字符串执行匹配。 重新反转匹配的字符串。

这具有能够正确匹配转义的打开标签的额外好处。

假设您有以下字符串; String \"this "should" NOT match\" and "this \"should\" match" 这里,\"this "should" NOT match\" 不应该匹配,"should" 应该匹配。 除此之外,this \"should\" match 应该匹配,\"should\" 不应该匹配。

先举个例子。

// The input string.
const myString = 'String \\"this "should" NOT match\\" and "this \\"should\\" match"';

// The RegExp.
const regExp = new RegExp(
    // Match close
    '([\'"])(?!(?:[\\\\]2)*[\\\\](?![\\\\]))' +
    '((?:' +
        // Match escaped close quote
        '(?:\\1(?=(?:[\\\\]2)*[\\\\](?![\\\\])))|' +
        // Match everything thats not the close quote
        '(?:(?!\\1).)' +
    ')0,)' +
    // Match open
    '(\\1)(?!(?:[\\\\]2)*[\\\\](?![\\\\]))',
    'g'
);

// Reverse the matched strings.
matches = myString
    // Reverse the string.
    .split('').reverse().join('')
    // '"hctam "\dluohs"\ siht" dna "\hctam TON "dluohs" siht"\ gnirtS'

    // Match the quoted
    .match(regExp)
    // ['"hctam "\dluohs"\ siht"', '"dluohs"']

    // Reverse the matches
    .map(x => x.split('').reverse().join(''))
    // ['"this \"should\" match"', '"should"']

    // Re order the matches
    .reverse();
    // ['"should"', '"this \"should\" match"']

好的,现在解释正则表达式。 这是正则表达式可以很容易地分成三部分。如下:

# Part 1
(['"])         # Match a closing quotation mark " or '
(?!            # As long as it's not followed by
  (?:[\\]2)* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)
# Part 2
((?:          # Match inside the quotes
(?:           # Match option 1:
  \1          # Match the closing quote
  (?=         # As long as it's followed by
    (?:\\\\)* # A pair of escape characters
    \\        # 
    (?![\\])  # As long as that's not followed by an escape
  )           # and a single escape
)|            # OR
(?:           # Match option 2:
  (?!\1).     # Any character that isn't the closing quote
)
)*)           # Match the group 0 or more times
# Part 3
(\1)           # Match an open quotation mark that is the same as the closing one
(?!            # As long as it's not followed by
  (?:[\\]2)* # A pair of escape characters
  [\\]         # and a single escape
  (?![\\])     # As long as that's not followed by an escape
)

这在图像形式上可能更清晰:使用Jex's Regulex生成

Image on github (JavaScript Regular Expression Visualizer.) 抱歉,我没有足够高的声誉来包含图片,所以,它现在只是一个链接。

以下是使用此概念的示例函数的要点,该概念更高级:https://gist.github.com/scagood/bd99371c072d49a4fee29d193252f5fc#file-matchquotes-js

【讨论】:

【参考方案15】:

我在尝试删除可能会干扰某些文件解析的带引号的字符串时遇到了类似的问题。

我最终得到了一个两步解决方案,它击败了您可以想出的任何复杂的正则表达式:

 line = line.replace("\\\"","\'"); // Replace escaped quotes with something easier to handle
 line = line.replaceAll("\"([^\"]*)\"","\"x\""); // Simple is beautiful

更容易阅读并且可能更高效。

【讨论】:

【参考方案16】:

如果您的 IDE 是 IntelliJ Idea,您可以忘记所有这些麻烦,将您的正则表达式存储到字符串变量中,当您将其复制粘贴到双引号内时,它会自动更改为正则表达式可接受的格式。

Java 中的示例:

String s = "\"en_usa\":[^\\,\\]+";

现在您可以在您的正则表达式或任何地方使用此变量。

【讨论】:

以上是关于带有转义引号的引用字符串的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式匹配双引号内的每个字符串并包含转义引号

使用正则表达式在 C# 中使用转义引号查找带引号的字符串

正则表达式在单引号内转义双引号

正则表达式 - 获取引号中的字符串忽略转义的引号和评论

使用正则表达式转义单引号字符串中的所有双引号 [重复]

使用正则表达式将带引号的字符串与嵌入的非转义引号匹配