Loading

pyparsing 学习

pyparsing

官方文档,可翻译查看
翻译Getting Started with Pyparsing

1. Pyparsing 程序的简单形式

  • ① import pyparsing 模块
  • ② 使用 pyparsing 类和帮助方法定义语法
  • ③ 使用语法解析输入文本
  • ④ 处理解析出的文本

2. 从 pyparsing 中引出名字

  • 不推荐 from pyparsing import * ,会污染本地变量命名空间。我们选择引入你需要的那些名字,eg,from pyparsing import Literal

3. 定义语法

  1. 步骤
  • 首先定义要匹配的标记和模式,然后将其分配给程序变量。也可以在此时定义可选的结果名称或解析操作。
  • 在此变量上调用parseString()或scanString(),传入要解析的字符串。在匹配过程中,默认情况下会跳过标记之间的空格(可以更改)。当令牌匹配发生时,将调用任何已定义的解析操作方法。
  • 处理解析的结果,作为ParseResults对象返回。可以像访问字符串列表一样访问ParseResults对象。如果在令牌模式的定义中使用定义了名称,则也可以将匹配结果作为返回结果的命名属性进行访问(setResultsName())。
  1. keys:
  • pyparsing模块的默认行为是忽略空格。该equation语法将成功地解析所有下面的语句:x=2+2x = 2+2a = 10 * 4r= 1234/ 100000
  • Combine 要求连续表达式在输入字符串中必须相邻,real = Combine(Word(nums) + '.' + Word(nums))
  • 表达式的重复可以使用*或[]表示。
① expr*3 is equivalent to expr + expr + expr
② expr[2, 3] is equivalent to expr + expr + Optional(expr)
③ expr[n, ...] or expr[n,] is equivalent to expr*n + ZeroOrMore(expr) (read as “at least n instances of expr”)
④ expr[... ,n] is equivalent to expr*(0, n) (read as “0 to n instances of expr”)
⑤ expr[...] and expr[0, ...] are equivalent to ZeroOrMore(expr)
⑥ expr[1, ...] is equivalent to OneOrMore(expr)
  • Or 表达式将评估所有指定的子表达式,以确定哪个是“最佳”匹配,即哪个匹配输入数据中的最长字符串。如果出现平局,则Or列表中最左边的表达式将获胜。
  • 如果解析整个文件的内容,请使用以下命令将其传递给 parseFile方法:expr.parseFile(sourceFile)
  • ParseExceptions将报告预期的标记或表达式未能匹配的位置。

4. 关键模块

  1. Word :一个或多个连续字符,Word也可以使用以下任意可选参数构造:① min -指示匹配字符的最小长度 ② max -指示匹配字符的最大长度 ③ exact -指示匹配字符的确切长度 eg,标识符,Word(alphas+"_", alphanums+"_")
  2. Char:一种便利形式Word,它将仅匹配字符串中的单个字符。eg,variable = Char(alphas) # such as x, z, m, etc.
  3. oneOfarithOp = oneOf("+ - * /") # arithmetic operators
  4. use operators such as +, |, ^, and ~ to combine expressions: equation = variable + "=" + integer + arithOp + integer # will match "x=2+2", etc.
  5. Combinereal = Combine(Word(nums) + '.' + Word(nums))
  6. Group:使用Group该类可将标记的逻辑组包含在子列表中。
  7. setResultsName:使用其字段名称访问令牌要容易得多,
stats = ("AVE:" + realNum.setResultsName("average")
         + "MIN:" + realNum.setResultsName("min")
         + "MAX:" + realNum.setResultsName("max"))
# 可以更简单,更干净地写成这样:
stats = ("AVE:" + realNum("average")
         + "MIN:" + realNum("min")
         + "MAX:" + realNum("max"))
  1. parseString(sourceString, parseAll=False):在整体匹配模式下仅调用一次;返回一个ParseResults对象;如果parseAll设置为True,则如果语法未处理完整的输入字符串,则parseString将引发ParseException。从定义上看,如果这个文本可以应用多次规则也只会运用到第一次上)
  2. searchString ,返回你给定文本的全部解析结果,放在一个列表中。
  3. parseFile(sourceFile):接受输入文件对象或文件名,也支持parseAll参数。
  4. scanString(sourceString):对于每个匹配的文本,返回一个元组:① 匹配的标记 ② 给定源字符串中匹配文本的开始位置 ③ 给定源字符串中的结束位置
  5. setName(name):为此元素关联一个简短的描述性名称,和setResultsName注意区分,这更多是为了显示异常和跟踪信息。eg,digits = Word(nums).setName("numeric digits")
  6. runTests(testsString):关于表达式的有用的开发和测试方法,以传递多行示例字符串来对表达式进行测试。#可以插入注释行(以开头),它们将包含在测试输出中
digits = Word(nums).setName("numeric digits")
real_num = Combine(digits + '.' + digits)
real_num.runTests("""\
    # valid number
    3.14159

    # no integer part
    .00001
    """)
# 输出
# valid number
3.14159
['3.14159']

# no integer part
.00001
^
FAIL: Expected numeric digits, found '.'  (at char 0), (line:1, col:1)
  1. setResultsName(string, listAllMatches=False):赋予与元素匹配的令牌的名称,如果重复组中有多个标记(例如ZeroOrMore或delimitedList),则默认值为仅返回最后一个匹配标记;如果listAllMatches设置为True,则返回所有匹配标记的列表。expr.setResultsName("key")也可以被写入expr("key") 。
  2. setParseAction(*fn):指定成功匹配元素后要调用的一个或多个函数。eg,intNumber = Word(nums).setParseAction(lambda s, l, t: [int(t[0])])
  3. setWhitespaceChars(chars):在尝试匹配特定的ParserElement之前,将要忽略的字符集定义为whitespace ,以代替默认的whitespace集 (space, tab, newline, and return)
  4. setDefaultWhitespaceChars(chars):为所有随后创建的ParserElement(包括副本)覆盖默认的空白字符集;
  5. suppress():用于抑制给定元素的输出,而不是用Suppress对象包装它。
# 以下等价
comma = Suppress(Literal(","))
comma = Literal(",").suppress()
comma = Suppress(",")
  1. Literal :使用要完全匹配的字符串进行构造
  2. CaselessLiteral:构造要匹配的字符串,但不进行大小写检查
  3. SkipTo:跳过输入字符串,接受直到指定模式的所有字符,用于指定不完整​​的语法。
  4. originalTextFor(expr):用于保留原始解析的文本,而不管包含的表达式进行的任何标记处理或转换如何。
fullName = originalTextFor(Word(alphas) + Word(alphas))
print(fullName.parseString("John Smith"))
# ['John Smith'],而不是 ['John','Smith']
  1. alphas :string.letters
  2. nums : string.digits
  3. alphanums :alphas + nums
  4. cStyleComment:由'/'和'/'序列分隔的注释块;可以跨越多行,但不支持注释的嵌套。eg,cFunction.ignore(cStyleComment)
  5. htmlComment:由''序列分隔的注释块;可以跨越多行,但不支持注释的嵌套
  6. quotedString :移除字符串的引号,quotedString .setParseAction(lambda t: t[0][1:-1])
  7. Forward:用于定义递归token模式的占位token;当之后在程序中定义实际的表达式时,使用 <<= 操作符将其插入到 Forward对象中。eg,
factor = Forward()
factor <<= atom + (expop + factor).setParseAction(push_first)[...]

5. 实例代码

import pyparsing as pp

greet = pp.Word(pp.alphas) + "," + pp.Word(pp.alphas) + "!"
for greeting_str in [
            "Hello, World!",
            "Bonjour, Monde!",
            "Hola, Mundo!",
            "Hallo, Welt!",
        ]:
    greeting = greet.parseString(greeting_str)
    print(greeting)
from pyparsing import Word, Group, Combine, Suppress, OneOrMore, alphas, nums, \
alphanums, stringEnd, ParseException
import time

num = Word(nums)
data = Combine(num + "/" + num + "/" + num)

def validateDateString(tokens):
  try:
    time.strptime(tokens[0], "%m/%d/%Y")
  except ValueError,ve:
    raise ParseException("Invalid data string (%s)" % tokens[0])

data.setParseAction(validateDateString)  # 触发这个函数进行检查
schoolName = OneOrMore(Word(alphas))
schoolName.setParseAction(lambda tokens: " ".join(tokens))
score = Word(nums).setParseAction(lambda tokens: int(tokens[0]))  # 转为数字
schoolndScore = Group(schoolName.setResultsName("school") + score.setResultsName("score"))
gameResult = data.setResultsName("date") + schoolndScore.setResultsName("team1") + schoolndScore.setResultsName("team2")

posted @ 2020-11-01 00:38  戴墨镜的长颈鹿  阅读(2068)  评论(0编辑  收藏  举报