在整数字节表示形式上进行pyparsing范围解析

问题描述

我想知道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