问题描述
我想知道pyparsing
是否可以解析和检测(以一种简单的方式)用几个字节表示的整数范围。这是一段代码,我可以用它来解析整数部分,然后用它做一些事情(只需用后面的内容打印出来):
from pyparsing import *
import struct
import re
min = 0x06A1D58C # 111_269_260
max = 0x14B4CB1C # 347_392_796
line_a = b'3F\x09\x21\xe4\xc0KBHDVC'
ParserElement.setDefaultWhitespaceChars("")
expr = Suppress('3F') + Regex(re.compile(r'.{4}',re.DOTALL)).setResultsName('id') + Word(
srange('[A-Z]')).setResultsName('code')
expr.parseWithTabs()
try:
result = expr.parseString(line_a.decode('latin-1'),parseAll=False)
print(result.get('id').encode('latin-1'))
id= struct.unpack('!I',result.get('id').encode('latin-1'))[0]
code = result.get('code')
if min <= id <= max:
print(id,code)
except ParseException as e:
print(e.explain(e))
输出:
b'\t!\xe4\xc0'
153216192 KBHDVC
现在我想要的是能够有一个表达式,该表达式将以整数形式指定范围以及后面的内容。这样,可以根据此整数指定几种语法。
这可能吗?还是必须将其保留在解析之外,以进行后期处理?
解决方法
如果您希望将这种转换和验证作为表达式定义的一部分进行,则可以添加一个解析时回调或 parse action
:binary_bytes = Regex(re.compile(r'.{4}',re.DOTALL))
def unpack(tokens):
return struct.unpack('!I',tokens[0].encode('latin-1'))[0]
binary_bytes.addParseAction(unpack)
解析操作可以获取解析的令牌并返回转换后的值或增值。
您还可以使用以下解析操作来实现类似于范围检查的过滤器:
def in_range(tokens):
if not (min <= tokens[0] <= max):
raise ParseException()
这种过滤器或验证器很常见,您可以使用addCondition对其进行定义:
binary_bytes.addCondition(lambda tokens: min <= tokens[0] <= max)
我重新格式化并重新打包了您的示例,如下所示:
def make_range_condition(minval,maxval):
in_range = lambda x,minval=minval,maxval=maxval: minval <= x <= maxval
return lambda t: in_range(t[0])
binary_bytes = Regex(re.compile(r'.{4}',re.DOTALL))
binary_bytes.addParseAction(lambda tokens: struct.unpack('!I',tokens[0].encode('latin-1'))[0])
binary_bytes.addCondition(make_range_condition(min,max))
ParserElement.setDefaultWhitespaceChars("")
expr = (Suppress('3F')
+ binary_bytes('id')
+ Word(srange('[A-Z]'))('code')
)
expr.parseWithTabs()
try:
result = expr.parseString(line_a.decode('latin-1'),parseAll=False)
print(result.dump())
except ParseException as e:
print(e.explain(e))
dump()给出以下输出:
[153216192,'KBHDVC']
- code: 'KBHDVC'
- id: 153216192