【问题标题】:How to scan for a string literal allowing escaped characters?如何扫描允许转义字符的字符串文字?
【发布时间】:2016-09-19 13:50:03
【问题描述】:

我想解析一个输入字符串并确定它是否包含被双引号 (") 包围的字符序列。 字符序列本身不允许包含更多的双引号,除非它们被反斜杠转义,例如:\"

为了让事情变得更复杂,反斜杠可以自己转义,如下所示:\\。因此,不会转义前面有两个(或任何偶数个)反斜杠 (\\") 的双引号。 更糟糕的是,允许使用单个非转义反斜杠(即后跟既不" 也不\)。

我正在尝试使用 Python 的 re 模块来解决这个问题。 module documentation 告诉我们有关管道运算符A|B

扫描目标字符串时,从左到右尝试由'|' 分隔的RE。当一个模式完全匹配时,该分支被接受。这意味着一旦A 匹配,B 将不会被进一步测试,即使它会产生更长的整体匹配。换句话说,'|' 运算符永远不会贪婪。

但是,这并没有像我预期的那样工作:

>>> import re
>>> re.match(r'"(\\[\\"]|[^"])*"', r'"a\"')
<_sre.SRE_Match object; span=(0, 4), match='"a\\"'>

这个正则表达式的想法是首先检查转义字符(\\\"),只有在没有找到时,才检查任何不是 " 的字符(但它可能是单个 @987654340 @)。 这可以发生任意次数,并且必须用文字 " 字符包围。

我希望字符串 "a\" 根本不匹配,但显然它匹配。 我希望\" 匹配A 部分和B 部分不被测试,但显然它是。

我真的不知道在这种情况下回溯是如何工作的,但是有没有办法避免它?

我想如果我在单独的步骤中首先检查初始 " 字符(并将其从输入中删除),它会起作用。 然后我可以使用以下正则表达式来获取字符串的内容:

>>> re.match(r'(\\[\\"]|[^"])*', r'a\"')
<_sre.SRE_Match object; span=(0, 3), match='a\\"'>

这将包括转义的引号。由于没有结束引号,我会知道总体而言,给定的字符串不匹配。

我必须这样做还是可以用一个正则表达式解决这个问题而无需额外的手动检查?

在我的实际应用程序中,"-enclosed 字符串只是较大模式的一部分,因此我认为在单个正则表达式中一次完成所有操作会更简单。

我发现了类似的问题,但那些不认为单个非转义反斜杠可以是字符串的一部分:regex to parse string with escaped charactersParsing for escape characters with a regular expression

【问题讨论】:

  • 看这个:"(?:[^\\"]|\\.)*" --> "[^"\\]*(?:\\.[^"\\]*)*"
  • 谢谢,效果很好!你说的箭头是什么意思?其中一种选择是否比另一种更好?它们完全等效吗?
  • 是的,有区别。好吧,让我把它作为答案,因为这对你有用。

标签: python regex parsing escaping


【解决方案1】:

当您使用"(\\[\\"]|[^"])*" 时,您匹配",后跟0+ 个\ 序列,后跟\",或非",然后是“关闭” "。请注意,当您的输入为 "a\" 时,\ 与第二个替代分支 [^"] 匹配(因为反斜杠是有效的非 ")。

您需要从非" 中排除\

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

所以,我们匹配",然后是非" 和非\(带有[^\\"])或任何转义序列(带有\\.),0 次或更多次。

然而,这个正则表达式不够高效,因为有很多回溯(由交替和量词引起)。展开版本是:

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

regex demo

最后一个模式匹配:

  • " - 双引号
  • [^"\\]* - 除\" 之外的零个或多个字符
  • (?:\\.[^"\\]*)* - 零个或多个序列
    • \\. - 反斜杠后跟除换行符以外的任何字符
    • [^"\\]* - 除了\" 之外的零个或多个字符
  • " - 双引号

【讨论】:

  • 这绝对是要走的路,但我仍然想知道为什么我的原始表达式匹配\而不是\",后者是A(在|的左侧)。文档说“一旦A 匹配,B 将不会被进一步测试”。在这种情况下,B 怎么匹配?​​
  • 答案是:回溯使得[^"] 可以匹配\,而不是\"。您的\\["\\] 是第一个,在"a[^"] 的2 次通过匹配之后,\" 被第一个替代方案抓取。但是,最后一个"强制,这意味着正则表达式引擎将尝试重新组合它迄今为止找到的捕获值以容纳" 的某个位置。引擎回溯,并在" 之前找到一个\,它可以与第二个替代分支[^"] 匹配。请参阅 regex 调试器 页面上的your regex in action,步骤 12、13。
  • 谢谢你的解释,现在我想我明白了!我对整个 regex 很陌生,我不知道 regex 调试器,它是了解这些 regex 如何工作的非常好的工具。
  • 太好了,我能帮上忙。正则表达式中的回溯既好又坏,你很快就会意识到这一点。不幸的是,re Python 模块不支持所有格量​​词,也不支持原子组(尽管您可以将它们与PyPi regex module 一起使用),那么您可以防止这种情况,例如"(\\[\\"]|[^"])*+"。另一种方法是通过反向引用/在正向前瞻中捕获:"(?:(?=(\\[\\"]|[^"]))\1)*"(即使使用 Python re 也可以使用)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 2020-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多