簡體   English   中英

使用pyparsing,如何解析以反斜杠結尾的帶引號的字符串

[英]With pyparsing, how do you parse a quoted string that ends with a backslash

我正在嘗試使用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\\

# 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 QuotedString反斜杠視為更通用的轉義。

有一種簡單的方法可以做到這一點,我忽略了嗎? .setParseAction(...)一種解決方法是使用.setParseAction(...)來處理事后的雙反斜杠 - 也許是這樣,這似乎有效:

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

我認為你誤解了escQuote的使用。 根據文件

escQuote - 用於轉義嵌入式引號字符串的特殊引號序列(例如SQL的“”以轉義嵌入式“)(默認值=無)

所以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 \\ or two )或one string \\'后面的無效one string \\'one string \\'和非字符串標記or two后跟一個不匹配的引號)。 在許多解析上下文中,這種情況是不可取的; 您希望關於字符串開始和結束的位置可在本地確定的決定,而不是在文檔后期更多地依賴於其他標記的出現。

PyParsing的QuotedString解析器不處理以反斜杠結尾的帶引號的字符串。 這是一個基本的限制,沒有任何簡單的解決方法,我可以看到。 如果你想支持那種字符串,你需要使用QuotedString以外的QuotedString

這也不是一個不常見的限制。 Python本身不允許在“原始”字符串文字的末尾使用奇數個反斜杠。 試一試: r"foo\\"會引發異常,而r"bar\\\\"將在輸出中包含兩個反斜杠。

您從當前代碼中獲取截斷輸出(而不是異常)的原因是因為您將反斜杠作為escQuote參數傳遞。 我認為這是一個替代指定轉義字符,而不是補充。 發生的事情是第一個反斜杠被解釋為一個內部引用(它無法解釋),並且由於它后跟一個實際引用字符,解析器認為它已到達引用字符串的末尾。 因此,你得到了ab'作為你的結果。

這段代碼不適合你的是什么?

from pyparsing import *

s = r"foo = 'ab\'cd\\'"  # <--- IMPORTANT - use a raw string literal here

ident = Word(alphas)
strValue = QuotedString("'", escChar='\\')
strAssign = ident + '=' + strValue

results = strAssign.parseString(s)
print results.asList() # displays repr form of each element

for r in results:
    print r # displays str form of each element

# count the backslashes
backslash = '\\'
print results[-1].count(backslash)

打印:

['foo', '=', "ab'cd\\\\"]
foo
=
ab'cd\\
2

編輯:

所以“\\”“變成”“”,但“\\”被解析但保持為“\\”而不是轉義為“\\”。 看起來像QuotedString中的一個錯誤。 現在您可以添加此解決方法:

import re
strValue.setParseAction(lambda t: re.sub(r'\\(.)', r'\g<1>', t[0]))

這將采取每個轉義的字符序列,只返回轉義字符,沒有前導'\\'。

我將在下一個pyparsing的補丁版本中添加它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM