python - 在pyparsing中使用特定长度的字段标记字符串
问题描述
我正在为 ascii 数据编写一个简单的解析器,其中每一行都必须被解释为 8 个字符块的字段:
"""
|--1---||--2---||--3---||--4---||--5---||--6---||--7---||--8---||--9---|
GRID 119 18.27 562.33 528.87
"""
这一行,应解释为:
1: GRID + 4 blank spaces
2: 5 blank spaces + 119
3: 8 blank spaces
4: 3 blank spaces + 18.27
5: 2 blank spaces + 562.33
6: 2 blank spaces + 528.87
7: 8 blank spaces
8: 8 blank spaces
9: 8 blank spaces
这是我尝试过的
EOL = LineEnd().suppress()
card_keyword = Keyword("GRID").leaveWhitespace().suppress()
number_card_fields = (number + ZeroOrMore(White()))
empty_card_fields = 8 * White()
card_fields = (number_card_fields | empty_card_fields)
card = (card_keyword + OneOrMore(card_fields)).setParseAction(self._card_to_dict)
def _card_to_dict(self, toks):
_FIELDS_MAPPING = {
0: "id", 1: "cp", 2: "x1", 3: "x2", 4: "x3", 5: "cd", 6: "ps", 7: "seid"
}
mapped_card = {self._FIELDS_MAPPING[idx]: token_field for idx, token_field in enumerate(toks)}
return mapped_card
test2 = """
GRID 119 18.27 562.33 528.87
"""
print(card.searchString(test2))
这次回归
[[{'id': 119, 'cp': ' ', 'x1': 18.27, 'x2': ' ', 'x3': 562.33, 'cd': ' ', 'ps': 528.87, 'seid': ' \n'}]]
我想获得这个,而不是
[[{'id': 119, 'cp': ' ', 'x1': 18.27, 'x2': 562.33, 'x3': 528.87, 'cd': ' ', 'ps': ' ', 'seid': ' '}]]
我认为问题就在这里number_card_fields = (number + ZeroOrMore(White()))
。我不知道如何告诉 pyparsing 这个表达式必须是 8 个字符长。
有人可以帮助我吗?提前感谢您的宝贵支持
解决方案
Pyparsing 允许您指定精确长度的单词。由于您的行是固定大小的字段,因此您的“单词”由任何可打印或空格字符组成,精确大小为 8:
field = Word(printables + " ", exact=8)
这是您的输入行的解析器:
import pyparsing as pp
# clear out whitespace characters - pretty much disables whitespace skipping
pp.ParserElement.setDefaultWhitespaceChars('')
# define an expression that matches exactly 8 printable or space characters
field = pp.Word(pp.printables + " ", exact=8).setName('field')
# a line has one or more fields
parser = field[1, ...]
# try it out
line = "GRID 119 18.27 562.33 528.87"
print(parser.parseString(line).asList())
印刷:
['GRID ', ' 119', ' ', ' 18.27', ' 562.33', ' 528.87']
我觉得这些空格很烦人,所以我们可以在字段中添加一个解析操作来去除它们:
# add a parse action to field to strip leading and trailing spaces
field.addParseAction(lambda t: t[0].strip())It
print(parser.parseString(line).asList())
现在给出:
['GRID', '119', '', '18.27', '562.33', '528.87']
看起来您希望总共有 8 个字段,并且您希望将数字字段转换为浮点数。这是您的_card_to_dict
解析操作的一个模式:
def str_to_value(s):
if not s:
return None
try:
return float(s)
except ValueError:
return s
def _card_to_dict(toks):
_FIELDS_MAPPING = {
0: "id", 1: "cp", 2: "x1", 3: "x2", 4: "x3", 5: "cd", 6: "ps", 7: "seid"
}
# this is one way to do it, but you can just add the names to toks
# mapped_card = {self._FIELDS_MAPPING[idx]: token_field for idx, token_field in enumerate(toks)}
for idx, token_field in enumerate(toks):
toks[_FIELDS_MAPPING[idx]] = str_to_value(token_field)
parser.addParseAction(_card_to_dict)
result = parser.parseString(line)
您可以将此结果转换为字典:
print(result.asDict())
印刷:
{'cd': 528.87, 'x2': 18.27, 'id': 'GRID', 'cp': 119.0, 'x1': None, 'x3': 562.33}
如果您使用以下方式转储结果:
print(result.dump())
你会得到:
['GRID', '119', '', '18.27', '562.33', '528.87']
- cd: 528.87
- cp: 119.0
- id: 'GRID'
- x1: None
- x2: 18.27
- x3: 562.33
这显示了如何直接访问解析结果,而无需转换为 dict:
print(result['x2'])
print(result.id)
印刷
18.27
GRID
推荐阅读
- r - 当差异很小时比较 2 个向量
- unity3d - 在 Unity 2D 中创建跷跷板,我应该以某种方式旋转铰链吗?
- c# - MVC 如何在 ASP.NET MVC 中将业务逻辑与 UI 分离?
- excel - 使用数据透视表比较值
- orm - 使用 update-database -script 生成脚本时,它还会为 _MigrationHistory 创建创建脚本?
- xml - 如何构建样式表以与 xsltproc 一起使用以将 xml 转换为 csv
- regex - 正则表达式匹配字符串中与字母相等的数字计数
- python - 计算每个熊猫行中从一个值到另一个值的变化次数并将所有计数相加
- vb.net - 值未传递给 Visual Basic 中的函数
- c++ - 通过分而治之查找子串