PyParsing的新手。我正在尝试解决如何解析xdot文件中的draw(和类似)属性。有许多项目,其中后续元素的数量在开始时以整数形式给出 - 类似于NetStrings。我已经看了一些示例代码来处理类似构造的netstring,但它似乎对我没用。
以下是一些示例:
具有3个点的多边形(P之后的3表示后面的点数):
P 3 811 190 815 180 806 185
应解析为'P', [[811, 190], [815, 180], [806, 185]]
2点多边形:
P 2 811 190 815 180 806 185
应解析为'P', [[811, 190], [815, 180]]
(结尾处有未解析的文字)
笔填充颜色(C后面的4表示消耗' - '后的字符数):
C 4 -blue
应解析为'C', 'blue'
更新信息:
我认为通过将这些例子放在他们自己的行上而没有更多的背景来误导我。这是一个真实的例子:
S 5 -solid S 15 -setlinewidth(1) c 5 -black C 5 -black P 3 690 181 680 179 687 187
有关实际规格,请参阅http://www.graphviz.org/doc/info/output.html#d:xdot。
请注意,文本字段中可能存在大量空格 - 上面的setlinewidth(1)可能是“abcd efgh hijk”,只要它恰好是15个字符,它就应该与'S'标记链接。在'P'标签之后应该有正好7个数字(初始计数器+ 3对),其他任何东西都应该引发解析错误,因为可能有更多的标签跟在(在同一条线上),但数字本身不是有效的。
希望这会让事情变得更加清晰。
答案 0 :(得分:1)
嗯,这就是我最后提出的,使用scanString。
int_ = Word(nums).setParseAction(lambda t: int(t[0]))
float_ = Combine(Word(nums) + Optional('.' + ZeroOrMore(Word(nums, exact=1)))).setParseAction(lambda t: float(t[0]))
point = Group(int_ * 2 ).setParseAction(lambda t: tuple(t[0]))
ellipse = ((Literal('E') ^ 'e') + point + int_ + int_).setResultsName('ellipse')
n_points_start = (Word('PpLBb', exact=1) + int_).setResultsName('n_points')
text_start = ((('T' + point + int_*3 ) ^ ('F' + float_ + int_) ^ (Word('CcS') + int_) ) + '-').setResultsName('text')
xdot_attr_parser = ellipse ^ n_points_start ^ text_start
def parse_xdot_extended_attributes(data):
results = []
while True:
try:
tokens, start, end = xdot_attr_parser.scanString(data, maxMatches = 1).next()
data = data[end:]
name = tokens.getName()
if name == 'n_points':
number_to_get = int(tokens[-1])
points, start, end = (point * number_to_get).scanString(data, maxMatches = 1).next()
result = tokens[:1]
result.append(points[:])
results.append(result)
data = data[end:]
elif name == 'text':
number_to_get = int(tokens[-2])
text, data = data[:number_to_get], data[number_to_get:]
result = tokens[:-2]
result.append(text)
results.append(result)
else:
results.append(tokens)
except StopIteration:
break
return results
答案 1 :(得分:1)
为了回应OP的编辑,以下答案不再完整。
我将尝试在这里找到你问题的核心并忽略更精细的细节。希望它能让你走上正确的语法轨道。基本上你要问的是,给出两行:
P 3 811 190 815 180 806 185
P 2 811 190 815 180 806 185
如何解析数据,以便在第二行只读取两个点?就个人而言,我会阅读所有数据并进行后解析。如果您命名结果,您可以让自己的工作变得无比轻松。例如:
from pyparsing import *
EOL = LineEnd().suppress()
number = Word(nums).setParseAction(lambda x: int(x[0]))
point_pair = Group(number + number)
poly_flag = Group(Literal("P") + number("length"))("flag")
poly_type = poly_flag + Group(OneOrMore(point_pair))("data")
xdot_line = Group(poly_type) + EOL
grammar = OneOrMore(xdot_line)
请注意,我们有data, flag
和length
名称,稍后会派上用场。让我们解析并处理字符串:
S = "P 3 811 190 815 180 806 185\nP 2 811 190 815 180 806 185\n"
P = grammar.parseString(S)
for line in P:
L = line["flag"]["length"]
while len(line["data"]) > L:
line["data"].pop()
提供有用且结构化的结果:
[['P', 3], [[811, 190], [815, 180], [806, 185]]]
[['P', 2], [[811, 190], [815, 180]]]
从这里开始,您可以逐个独立地构建语法部分。每次添加新类型时,请将其添加到xdot_line
,即
xdot_line = Group(poly_type | pen_fill_type) + EOL