使用 pyparsing 解析字节字符串
Parsing a byte string with pyparsing
我有来自网络的数据包,我想使用 pyparsing
来检测消息并提取不同数据包类型中的特定数据。当然,来自网络的是字节串的形式,如下例所示。
b'\x03\xff*******************************************************************'
其中 *
代表任意字符。请注意,没有使用特定的编码,例如 Unicode
.
我可以将 pyparsing
与字节字符串一起使用,当我明确指定要查找的内容时似乎工作正常,例如:
expr = Suppress(b'\x03\xff')
现在我想让它找到一个 20 字节的序列,例如放在 Suppress(b'\x03\xff')
之后,它们可以是任何东西。如果可以使用 DOTALL 标志,我想使用像 Regex('.{20}')
这样的表达式,但无论如何我得到了错误:
TypeError: cannot use a string pattern on a bytes-like object
那么我怎样才能检测到这个 20 字节的序列呢?
附录
尝试@FMc 提议:
from pyparsing import *
expr = Suppress(b'\x03\xff') + Regex(b'.{20}')
line = b'\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff--4353425352FGDSGSFDGBFSDBGfdeGRES'
print(expr.parseString(line, parseAll=False).dump())
给予
Traceback (most recent call last):
File "<input>", line 2, in <module>
File "lib\site-packages\pyparsing\core.py", line 2384, in __init__
self.mayReturnEmpty = self.re_match("") is not None
TypeError: cannot use a bytes pattern on a string-like object
我不确定,但我怀疑 pyparsing 是 文本解析器,而不是字节解析器。我在文档中没有提到“二进制”或“字节”。 docs中的第一段:
This document provides how-to instructions for the pyparsing library,
an easy-to-use Python module for constructing and executing basic text
parsers.
当我在 pyparsing 代码库的失败行之前添加一个 print([expr])
时,打印了很多内容,给我的印象是 pyparsing 的代码库中包含 text-based 假设。这是全部爆炸之前的输出片段:
[<SP><TAB>]
[{{{~{","} ~{LineEnd}} W:(0123...)} [<SP><TAB>]}]
[{{{{~{","} ~{LineEnd}} W:(0123...)} [<SP><TAB>]}}...]
[{quotedString using single or double quotes | commaItem}]
[","]
[{Suppress:(",") [{quotedString using single or double quotes | commaItem}]}]
[b'\x03\xff']
Traceback (most recent call last):
File "x.py", line 3, in <module>
expr = Suppress(b'\x03\xff') + Regex(b'.{20}')
File "/Users/.../lib/python3.7/site-packages/pyparsing.py", line 5100, in __init__
super(TokenConverter, self).__init__(expr) # , savelist)
File "/Users/.../lib/python3.7/site-packages/pyparsing.py", line 4453, in __init__
self.mayIndexError = expr.mayIndexError
AttributeError: 'bytes' object has no attribute 'mayIndexError'
最初,我有一个想法使用编码方案(例如,十六进制)来解析字节
与pyparsing。但经过一些初步实验后,我意识到这条路似乎
复杂——而且可能行不通。 pyparsing 库与
正则表达式,在其 API 及其底层实现中。例如,这个
无法按预期工作:
from pyparsing import Regex, ParseException
# Your input converted to hex.
line = b'\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff--4353425352FGDSGSFDGBFSDBGfdeGRES'
hex_line = line.hex()
# An easy regex, naively converted to hex.
pattern = r'.{20}'
hex_pattern = pattern.encode().hex() # 2e7b32307d
rgx = Regex(hex_pattern)
# It doesn't work: the regex syntax has been lost. We end up searching for the
# literal 2e7b32307d, which isn't found.
try:
print(rgx.parseString(hex_line, parseAll = False).dump())
except ParseException as e:
print(e)
# A hex-minded regex for 20 arbitrary ACSII characters would be this:
hpattern = r'.{40}'
rgx = Regex(hpattern)
# This works.
print(rgx.parseString(hex_line, parseAll = False).dump())
但是那个例子太简单了。许多正则表达式概念不太方便
以十六进制表示:
\d+ # Regex
(?:3[0-9])+ # Hex-centric regex? No thanks.
除非你的需求很简单(而且,如果是这样,为什么还要使用 pyparsing),这个
道路似乎很艰难。再一次,不要忘记许多 pyparsing 的 API
元素在引擎盖下作为普通正则表达式实现。
看来 pyparsing
只适用于文本 (str
) 而不是 bytes
。
一个想法是使用 latin-1
(普通编码)将字节转换为“”“文本”””,然后使用它解析为文本:
from pyparsing import *
expr = Suppress('\x03\xff') + Regex('.{20}')
line = b'\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff--4353425352FGDSGSFDGBFSDBGfdeGRES'
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
输出:
$ python3 t.py
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff--435342535'
为了在执行之前进行测试,我尝试了 Anthony Sottile 的想法,在要测试的行中使用完整的字节谱,但出现以下错误:
from pyparsing import *
expr = Suppress('\x03\xff') + Regex('.{20}')
line = b'\x03\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
Traceback (most recent call last):
File "<input>", line 4, in <module>
File "\lib\site-packages\pyparsing.py", line 1955, in parseString
raise exc
File "\lib\site-packages\pyparsing.py", line 3342, in parseImpl
raise ParseException(instring, loc, self.errmsg, self)
pyparsing.ParseException: Expected Re:('.{20}'), found '\x00' (at char 2), (line:1, col:3)
要使其正常工作,我需要以下内容:
from pyparsing import *
import re
expr = Suppress('\x03\xff') + Regex(re.compile(r'.{256}', re.DOTALL))
line = b'\x03\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
print(len(tok), len(tok.encode('latin-1')))
输出:
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08 \n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb'
256 256
但是打印的字符串以 \xfb
结尾,我想知道为什么长度正好是 256。看起来 TAB=\x09
被转换为 5 个空格,所以转换在 4 个字符之前停止\xff。
我发现 parseString
将 TAB
转换为空格。所以通过添加 parseWithTabs()
一切正常:
from pyparsing import *
import re
expr = Suppress('\x03\xff') + Regex(re.compile(r'.{256}', re.DOTALL))
line = b'\x03\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
expr.parseWithTabs()
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
print(len(tok), len(tok.encode('latin-1')))
输出:
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
256 256
附录
实际上,为了确保它在每种情况下都能正常工作,需要更新默认的空白字符,以便它们在某些情况下不会被跳过。在这种情况下,我选择使用 setDefaultWhitespaceChars()
ParserElement.setDefaultWhitespaceChars("")
我有来自网络的数据包,我想使用 pyparsing
来检测消息并提取不同数据包类型中的特定数据。当然,来自网络的是字节串的形式,如下例所示。
b'\x03\xff*******************************************************************'
其中 *
代表任意字符。请注意,没有使用特定的编码,例如 Unicode
.
我可以将 pyparsing
与字节字符串一起使用,当我明确指定要查找的内容时似乎工作正常,例如:
expr = Suppress(b'\x03\xff')
现在我想让它找到一个 20 字节的序列,例如放在 Suppress(b'\x03\xff')
之后,它们可以是任何东西。如果可以使用 DOTALL 标志,我想使用像 Regex('.{20}')
这样的表达式,但无论如何我得到了错误:
TypeError: cannot use a string pattern on a bytes-like object
那么我怎样才能检测到这个 20 字节的序列呢?
附录
尝试@FMc 提议:
from pyparsing import *
expr = Suppress(b'\x03\xff') + Regex(b'.{20}')
line = b'\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff--4353425352FGDSGSFDGBFSDBGfdeGRES'
print(expr.parseString(line, parseAll=False).dump())
给予
Traceback (most recent call last):
File "<input>", line 2, in <module>
File "lib\site-packages\pyparsing\core.py", line 2384, in __init__
self.mayReturnEmpty = self.re_match("") is not None
TypeError: cannot use a bytes pattern on a string-like object
我不确定,但我怀疑 pyparsing 是 文本解析器,而不是字节解析器。我在文档中没有提到“二进制”或“字节”。 docs中的第一段:
This document provides how-to instructions for the pyparsing library, an easy-to-use Python module for constructing and executing basic text parsers.
当我在 pyparsing 代码库的失败行之前添加一个 print([expr])
时,打印了很多内容,给我的印象是 pyparsing 的代码库中包含 text-based 假设。这是全部爆炸之前的输出片段:
[<SP><TAB>]
[{{{~{","} ~{LineEnd}} W:(0123...)} [<SP><TAB>]}]
[{{{{~{","} ~{LineEnd}} W:(0123...)} [<SP><TAB>]}}...]
[{quotedString using single or double quotes | commaItem}]
[","]
[{Suppress:(",") [{quotedString using single or double quotes | commaItem}]}]
[b'\x03\xff']
Traceback (most recent call last):
File "x.py", line 3, in <module>
expr = Suppress(b'\x03\xff') + Regex(b'.{20}')
File "/Users/.../lib/python3.7/site-packages/pyparsing.py", line 5100, in __init__
super(TokenConverter, self).__init__(expr) # , savelist)
File "/Users/.../lib/python3.7/site-packages/pyparsing.py", line 4453, in __init__
self.mayIndexError = expr.mayIndexError
AttributeError: 'bytes' object has no attribute 'mayIndexError'
最初,我有一个想法使用编码方案(例如,十六进制)来解析字节 与pyparsing。但经过一些初步实验后,我意识到这条路似乎 复杂——而且可能行不通。 pyparsing 库与 正则表达式,在其 API 及其底层实现中。例如,这个 无法按预期工作:
from pyparsing import Regex, ParseException
# Your input converted to hex.
line = b'\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff--4353425352FGDSGSFDGBFSDBGfdeGRES'
hex_line = line.hex()
# An easy regex, naively converted to hex.
pattern = r'.{20}'
hex_pattern = pattern.encode().hex() # 2e7b32307d
rgx = Regex(hex_pattern)
# It doesn't work: the regex syntax has been lost. We end up searching for the
# literal 2e7b32307d, which isn't found.
try:
print(rgx.parseString(hex_line, parseAll = False).dump())
except ParseException as e:
print(e)
# A hex-minded regex for 20 arbitrary ACSII characters would be this:
hpattern = r'.{40}'
rgx = Regex(hpattern)
# This works.
print(rgx.parseString(hex_line, parseAll = False).dump())
但是那个例子太简单了。许多正则表达式概念不太方便 以十六进制表示:
\d+ # Regex
(?:3[0-9])+ # Hex-centric regex? No thanks.
除非你的需求很简单(而且,如果是这样,为什么还要使用 pyparsing),这个 道路似乎很艰难。再一次,不要忘记许多 pyparsing 的 API 元素在引擎盖下作为普通正则表达式实现。
看来 pyparsing
只适用于文本 (str
) 而不是 bytes
。
一个想法是使用 latin-1
(普通编码)将字节转换为“”“文本”””,然后使用它解析为文本:
from pyparsing import *
expr = Suppress('\x03\xff') + Regex('.{20}')
line = b'\x03\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff--4353425352FGDSGSFDGBFSDBGfdeGRES'
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
输出:
$ python3 t.py
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff--435342535'
为了在执行之前进行测试,我尝试了 Anthony Sottile 的想法,在要测试的行中使用完整的字节谱,但出现以下错误:
from pyparsing import *
expr = Suppress('\x03\xff') + Regex('.{20}')
line = b'\x03\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
Traceback (most recent call last):
File "<input>", line 4, in <module>
File "\lib\site-packages\pyparsing.py", line 1955, in parseString
raise exc
File "\lib\site-packages\pyparsing.py", line 3342, in parseImpl
raise ParseException(instring, loc, self.errmsg, self)
pyparsing.ParseException: Expected Re:('.{20}'), found '\x00' (at char 2), (line:1, col:3)
要使其正常工作,我需要以下内容:
from pyparsing import *
import re
expr = Suppress('\x03\xff') + Regex(re.compile(r'.{256}', re.DOTALL))
line = b'\x03\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
print(len(tok), len(tok.encode('latin-1')))
输出:
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08 \n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb'
256 256
但是打印的字符串以 \xfb
结尾,我想知道为什么长度正好是 256。看起来 TAB=\x09
被转换为 5 个空格,所以转换在 4 个字符之前停止\xff。
我发现 parseString
将 TAB
转换为空格。所以通过添加 parseWithTabs()
一切正常:
from pyparsing import *
import re
expr = Suppress('\x03\xff') + Regex(re.compile(r'.{256}', re.DOTALL))
line = b'\x03\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
expr.parseWithTabs()
for tok in expr.parseString(line.decode('latin-1'), parseAll=False):
print(tok.encode('latin-1'))
print(len(tok), len(tok.encode('latin-1')))
输出:
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
256 256
附录
实际上,为了确保它在每种情况下都能正常工作,需要更新默认的空白字符,以便它们在某些情况下不会被跳过。在这种情况下,我选择使用 setDefaultWhitespaceChars()
ParserElement.setDefaultWhitespaceChars("")