python - 使用 pyparsing,如何解析以反斜杠结尾的带引号的字符串

标签 python regex pyparsing quoting

我正在尝试使用 pyparsing在以下条件下解析带引号的字符串:

  • 带引号的字符串可能包含内部引号。
  • 我想使用反斜杠来转义内部引号。
  • 引用的字符串可能以反斜杠结尾。

我正在努力定义一个成功的解析器。另外,我开始怀疑 pyparsing 对这种带引号的字符串使用的正则表达式是否正确(请参阅下面的替代正则表达式)。

我是否(很可能)错误地使用了 pyparsing 或 pyparsing 中是否存在错误?

这是演示问题的脚本(注意:忽略此脚本;请关注下面的更新。):

import pyparsing as pp
import re

# A single-quoted string having:
#   - Internal escaped quote.
#   - A backslash as the last character before the final quote.
txt = r"'ab\'cd\'"

# Parse with pyparsing.
# Does not work as expected: grabs only first 3 characters.
parser = pp.QuotedString(quoteChar = "'", escChar = '\\', escQuote = '\\')
toks   = parser.parseString(txt)
print
print 'txt:    ', txt
print 'pattern:', parser.pattern
print 'toks:   ', toks

# Parse with a regex just like the pyparsing pattern, but with
# the last two groups flipped -- which seems more correct to me.
# This works.
rgx = re.compile(r"\'(?:[^'\n\r\\]|(?:\\.)|(?:\\))*\'")
print
print rgx.search(txt).group(0)

输出:

txt:     'ab\'cd\'
pattern: \'(?:[^'\n\r\\]|(?:\\)|(?:\\.))*\'
toks:    ["ab'"]

'ab\'cd\'

更新

感谢您的回复。我怀疑我的问题结构不当,弄糊涂了,所以让我再试一次。

假设我们正在尝试解析一种语言,这种语言通常像 Python 一样使用引用规则。我们希望用户能够定义可以包含内部引号(由反斜杠保护)的字符串,并且我们希望这些字符串能够以反斜杠结尾。这是我们语言的示例文件。请注意,该文件也会解析为有效的 Python 语法,如果我们打印 foo(在 Python 中),输出将是文字值:ab'cd\/p>

# demo.txt
foo = 'ab\'cd\\'

我的目标是使用pyparsing来解析这样的语言。有办法吗?上面的问题基本上是我在几次尝试失败后最终得出的结论。以下是我的初步尝试。它失败了,因为末尾有两个反斜杠,而不仅仅是一个。

with open('demo.txt') as fh:
    txt = fh.read().split()[-1].strip()

parser = pp.QuotedString(quoteChar = "'", escChar = '\\')
toks   = parser.parseString(txt)
print
print 'txt:    ', txt
print 'pattern:', parser.pattern
print 'toks:   ', toks             # ["ab'cd\\\\"]

我想问题在于 QuotedString 仅将反斜杠视为引号转义,而 Python 将反斜杠视为更通用的转义。

有没有一种我忽略的简单方法可以做到这一点?我想到的一种解决方法是在事后使用 .setParseAction(...) 来处理双反斜杠——也许像这样,这似乎可行:

qHandler = lambda s,l,t: [ t[0].replace('\\\\', '\\') ]
parser = pp.QuotedString(quoteChar = "'", escChar = '\\').setParseAction(qHandler)

最佳答案

我认为您误解了 escQuote 的用法。根据the docs :

escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None)

因此 escQuote 用于指定解析为文字引号的完整序列。例如,在文档中给出的示例中,您将指定 escQuote='""' 并将其解析为 "。通过将反斜杠指定为 escQuote,您导致单个反斜杠被解释为引号。您在示例中看不到这一点,因为您除了引号外没有转义任何内容。但是,如果您尝试转义其他内容,您会发现它不起作用:

>>> txt = r"'a\Bc'"
>>> parser = pyp.QuotedString(quoteChar = "'", escChar = '\\', escQuote = "\\")
>>> parser.parseString(txt)
(["a'Bc"], {})

请注意,反斜杠已替换为 '

至于您的选择,我认为 pyparsing(和许多其他解析器)不这样做的原因是它涉及字符串中的一个位置的特殊大小写。在您的正则表达式中,单个反斜杠在任何地方都是转义字符,除了作为字符串中的最后一个字符,在该位置它按字面意思处理。这意味着您无法“本地”判断给定的引号是否真的是字符串的结尾——即使它有一个反斜杠,如果后面没有反斜杠,它也可能不是结尾。这可能导致解析歧义和令人惊讶的解析行为。例如,请考虑以下示例:

>>> txt = r"'ab\'xxxxxxx"
>>> print rgx.search(txt).group(0)
'ab\'
>>> txt = r"'ab\'xxxxxxx'"
>>> print rgx.search(txt).group(0)
'ab\'xxxxxxx'

我通过在字符串末尾加一个撇号,一下子让前面的撇号不再是末尾,一下子把所有的x都加到字符串里了。在实际使用的上下文中,这可能会导致困惑的情况,在这种情况下,不匹配的引号会默默地导致字符串的重新解析而不是解析错误。

虽然我目前无法举出示例,但我也怀疑如果您实际尝试解析包含多个此类字符串的大型文档,这可能会导致“灾难性回溯”。 (这是我关于“100MB 的其他文本”的观点。)因为解析器无法在不进一步解析的情况下知道给定的 \' 是否是字符串的末尾,所以它可能不得不去一直到文件末尾,以确保那里没有更多的引号。如果文件的剩余部分包含此类型的其他字符串,则可能很难确定哪些引号分隔哪些字符串。例如,如果输入包含类似

'one string \' 'or two'

我们无法判断这是两个有效字符串(one string\ and or two)还是后面有无效内容的字符串(one string\' 和非字符串标记 或两个 后跟一个不匹配的引号)。这种情况在许多解析上下文中是不可取的;您希望关于字符串开始和结束位置的决定是本地可确定的,而不是依赖于文档后面很长一段时间内其他标记的出现。

关于python - 使用 pyparsing,如何解析以反斜杠结尾的带引号的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23305803/

相关文章:

python - 如何在 pyparsing 中迭代 ParseResults

ruby - 正则表达式中的 `?i` 和 `?-i` 是什么意思?

javascript - 如何在 'input' 上找到该值多次出现 '//'(正斜杠)的情况?

java - 如果子字符串遵循该模式,则正则表达式返回 true

python - 计算日期列表之间的平均天数

python - Pyparsing 忽略 except

python - 如何在 PyParsing 中构造一个相当于 FollowedBy 子类的 "leading"

python - 从 Python 脚本使用 POST 发送文件

python - py.test : Temporary folder for the session scope

Python 示例在测验期间而不是结束时重复答案