使用正则表达式解析嵌套组可能很困难、难以辨认或无法实现。
寻址嵌套组的一种方法是使用parsing grammar。这是 Eric Rose 使用 parsimonious 库的 3 步示例。
给定
import itertools as it
import parsimonious as pars
source = """\
/TT0 1 Tf
0.002 Tc -0.002 Tw 11.04 0 0 11.04 221.16 707.04 Tm
[(\()-2(Y)7(o)7(u )-3(g)7(o)-2(t )4(i)-3(t)(\))]TJ
EMC"""
代码
- 定义语法
rules = r"""
root = line line message end
line = ANY NEWLINE
message = _ TEXT (_ TEXT*)* NEWLINE
end = "EMC" NEWLINE*
TEXT = ~r"[a-zA-Z ]+"
NEWLINE = ~r"\n"
ANY = ~r"[^\n\r]*"
_ = meaninglessness*
meaninglessness = ~r"(TJ)*[^a-zA-Z\n\r]*"
"""
- 解析源文本并构建 AST
grammar = pars.grammar.Grammar(rules)
tree = grammar.parse(source)
# print(tree)
- 解决 AST
class Translator(pars.NodeVisitor):
def visit_root(self, node, children):
return children
def visit_line(self, node, children):
return node.text
def visit_message(self, node, children):
_, s, remaining, nl = children
return (s + "".join(it.chain.from_iterable(i[1] for i in remaining)) + nl)
def visit_end(self, node, children):
return node.text
def visit_meaninglessness(self, node, children):
return children
def visit__(self, node, children):
return children[0]
def visit_(self, node, children):
return children
def visit_TEXT(self, node, children):
return node.text
def visit_NEWLINE(self, node, children):
return node.text
def visit_ANY(self, node, children):
return node.text
演示
tr = Translator().visit(tree)
print("".join(tr))
输出
/TT0 1 Tf
0.002 Tc -0.002 Tw 11.04 0 0 11.04 221.16 707.04 Tm
You got it
EMC
详情
- 我们定义了一组类似于 regex/EBNF 的语法规则 see docs for details,而不是一个死板的(有时难以辨认的正则表达式)。定义语法后,如果需要,调整起来会容易得多。
- 注意:原始文本已被修改,在
2(t)(第 3 行)中添加了一个空格,因为它被认为是 OP 中缺失的。
- 解析步骤很简单。只需
parse 基于语法的源文本。如果语法定义充分,则会创建一个 AST,其中包含反映源结构的节点。拥有 AST 是关键,因为它使这种方法更加灵活。
- 定义访问每个节点时要执行的操作。可以使用任何所需的技术来解析 AST。例如,这里我们通过从
parsmonious 继承NodeVisitor 来演示Visitor Pattern。
现在,对于您的 PDF 中遇到的新文本或意外文本,只需修改语法并再次解析即可。