Using the pyparsing module - 使用pyparsing模块

1 操作步骤

为了解析输入的数据字符串,客户端代码必须遵循如下步骤:


1. 首先,定义要匹配的各种标记(token)和模式(pattern),并且将其赋值给程序变量。可选的结果名称和解析动作也可以被定义出来。


2. 基于上面的变量调用parseString()或scanString()并传入一个需要解析的字符串。在解析的过程中,whitespace字符会被忽略掉(当然也可以改变这种方式)。当标记被匹配时,其对应定义的解析行为方法将被调用。


3. 可以像处理字符串列表一样处理解析后的结果。如果标记调用setResultsName()设置了名字,匹配的结果也可以向字典那样用名字进行访问。


1.1 Hello,World!


下面用一个完整的python程序来解析"Hello,World",或其它符合"<问候>,<人>!"形式的问候语。

from pyparsing import Word, alphas
greet = Word( alphas ) + "," + Word( alphas ) + "!"
greeting = greet.parseString( "Hello, World!" )
print greeting


此些标记返回如下格式的内容:

['Hello', ',', 'World', '!']


1.2 使用注意事项

  • pyparsing模块可以被用来解析简单的名字字符串或代数表达式,同时也可以从复杂格式文本报告中提取数据。然后你定义的匹配模式也可能会接收到一个无效的格式输入。pyparsing用来从定义良好的数据格式中进行数据提取。


  • 为了代码的可读性,利用诸如'+','|','^'或'~'操作符进行表达式的连接。你也可以将一个字符串与解析表达式对象进行连接-这个字符串会自动的转换为Literal类型的对象,例如:


    integer  = Word( nums )            # simple unsigned integer
    variable = Word( alphas, max=1 )   # single letter variable, such as x, z, m, etc.
    arithOp  = Word( "+-*/", max=1 )   # arithmetic operators
    equation = variable + "=" + integer + arithOp + integer    # will match "x=2+2", etc.

    

    在equation变量的定义中,字符串'='会自动被解析为Literal('='),这种处理具有更好的可读性。


  • pyparsing默认的行为是忽略掉whitespace。这种方式满足99%的使用。这种处理可以让上面的equation编写出简单,干净,而不用考虑whitespace的乱入。equation语法可以成功的匹配以下语句:

    x=2+2
    x = 2+2
    a = 10   *   4
    r= 1234/ 100000

    

    同时,只要将上例进行简单的扩展,就可以支持更复杂的情况,利用括号的嵌套,浮点数,科学计数法,命名常量(例如e或pi)。请参考example目录下的fourFn.py。


  • 为了修改pyparing默认的忽略whitespace的处理方式,你可以使用以下方法:


使用静态方法ParserElement.setDefaultWhitespaceChars进行whitespace字符(空格,\r\n\t)的重载。例如当定义的语法中换行具有重要的含义,你可以调用ParserElement.setDefaultWhitespaceChars(' \t')将换行字符从whitespace中移除。通过这种方式的调用,将会影响到所有pyparsing表达式的定义。


基于单个的表达式调用leaveWhitespace()。


利用Combine处理多个表达式比较彼此相邻,中间不能有whitespace的情况。例如:

       real = Word(nums) + '.' + Word(nums)

    将匹配'3.14159',但同时也匹配'3 . 12'。并且他回返的匹配结果为['3', '.', '14159']。

    改变一下此表达式:

       real = Combine( Word(nums) + '.' + Word(nums) )

    它对数字中间有空格的情况不会匹配,同时返回一个连接起来的字符串'3.14159'作为匹配的结         果。


  • 重复的表达式定义可以利用'*'操作符。表达式可以通过制定一个整数(表示重复的次数)来表示重复,或通过数组定义两个整数,或None跟一个整数来表示最小和最大的重复次数。参考以下示例:


    expr*3 等于 to expr + expr + expr


    expr*(2,3) 等于 expr + expr + Optional(expr)


    expr*(n,None) 或 expr*(n,) 等于 expr*n + ZeroOrMore(expr) (至少n次匹配)


    expr*(None,n) 等于 expr*(0,n) (匹配0到n次)


    expr*(None,None) 等于 ZeroOrMore(expr)


    expr*(1,None) 等于 OneOrMore(expr)


    对于expr*(None,n),如果输入中存在多于n个expr的情况,是不会出现解析异常的,如果想得到一场可以写为expr*(None,n)+~expr。


  • MatchFirst表达式执行从左到右的匹配,如果第一个匹配出现,那么就忽略掉后续的表达式,故需要将特别指定(more-specific)的模式放于稍次于(less-specific)表达式之前。如果你无法确认,那么用'Or'表达式-它会始终匹配最长匹配的表达式,当然会浪费一些性能。


  • 'Or'表达式将解析所有的子表达式,并将匹配最长输入数据作为结果。如果出现平局,那么最左边的表达式将获胜。


  • 如果是对一个文件进行解析,那么可以调用expr.parseFile(sourcefile)方法。


  • ParseExceptions将报告表达式匹配出错的位置。例如,如果我们要对"Hello,World!"进行解析,那么如果是"Hello World!",我们将会得到一个异常,内容如下:


   pyparsing.ParseException: Expected "," (6), (1,7)

    

    在复杂的表达式中,只报告错误的位置可能是不够的。可以通过查看ParseException类定义来获取更好的内容。


  • 用Group类对一组有逻辑关系的匹配进行编组。这将帮助解析后的结果更具备结构层次。


  • 标点符号可会与进行显示的匹配,但其自身作为解析结果的一部分通过没什么意思。利用supress()方法可以将匹配的内容不包含的结果信息列表中。例如,delimitedList()匹配一系列利用分隔符分割的表达式,但只返回表达式匹配的内容,分隔符被排除在结果之外。


  • 解析行为可以用来将字符串转换为其他类型,例如int,float,boolean...


  • 对于复杂的表达式,设置结果的名字是被推荐的,通过字段的名字访问匹配结果的方法比对结果列表的内容进行访问简单很多,尤其是表达式中包括可选的部分中。你也可以简化对setResultsName的访问:

    stats = "AVE:" + realNum.setResultsName("average") + \
    "MIN:" + realNum.setResultsName("min") + \
    "MAX:" + realNum.setResultsName("max")

    可以写成:


    stats = "AVE:" + realNum("average") + \
    "MIN:" + realNum("min") + \
    "MAX:" + realNum("max")


  • 当利用解析行为对全局变量或数据结构进行修改时要格外的注意,尤其是低级别的标记或在and表达式中的子表达式;前一个表达式的匹配可能会导致后续的整体匹配失败。


  • pyparsing对于复杂的语法或庞大的输入处理可能会慢一些。psyco包可以在不修改代码的情况下提示20%-50%的性能。


2 类


2.1 pyparsing模块中的类


ParserElement - 所有pyparsing类的抽象基类;方法有:


  • parseString(sourceString,parseAll=False) - 对于输入从头到尾只执行一次匹配;返回一个ParseResults 对象,匹配的结果可以像列表一样访问,也可以选用访问字典的方式;如果parseAll设置为True,则对于输入的字符串无法全部解析的情况将抛出异常。


  • parseFile(sourceFile) - 处理输入文件对象或文件名比较方便。文件内容将被当然一个字符串传给parseString()。parseFile同时也支持parseAll参数。


  • scanString(sourceString) - 生成器函数, 在输入的字符串中查找和提取文本;对于任意的被匹配文本,都将返回如下元组:


    匹配标记(ParseResults对象)


    匹配的起始位置


    匹配的结束位置


    scanString允许随机匹配,而不会想parseString那样严格的进行匹配。


  • transformString(sourceString) - 对scanString进行便利的封装,将输入字符串中被匹配的部分,利用解析行为进行内容替换。


  • searchString(sourceString) - 另一个对scanString的封装,返回每次scanString产生的ParseResults对象的列表。


  • setName(name) - 给予一个名称,在发生异常和产生trace信息时,显示的更清晰。


  • setResultsName(string,listAllMatches=False) - 给予一个名称;如果此表达式被重复组(像ZeroOrMore或者delimitedList)修饰,默认只返回最后的匹配结果 - 如果listAllMatches设置为True,那么将会返回所有的结果。


  • setParseAction(*fn) - 指定一个或多个函数在匹配成功后被调用;每个函数被定义为fn(s,loc,toks):


    s是输入字符串


    loc是匹配的开始位置


    toks是匹配结果的列表


    多个函数通过多个setParseAction参数进行指定,或者也可以调用setParseAction多次。


    每个解析行为函数为了进行转换或修改字符串内容,可以修改toks并返回。例如,fn也可以利用一个lambda函数来进行字符串转整形的操作:

intNumber = Word(nums).setParseAction( lambda s,l,t: [ int(t[0]) ] )

    如果fn不需要修改toks列表,它就不必返回。


  • setBreak(breakFlag=True) - 如果设置breakFlag为True,则在发生异常时调用pdb.set_break()。


  • copy() - 返回一个ParserElement对象的拷贝;可以将相同的表达式携带不同的解析行为用于语法的不同部分。


  • leaveWhitespace() - 改变默认的忽略whitespace行为。


  • setWhitespaceChars(chars) - 定义一个字符集合当作whitespace处理。


  • suppress() - 阻止无用元素的匹配输出,对Suppress对象的封装。


  • ignore(expr) - 指定匹配过程中需要忽略的表达式;在多次匹配过程中重复的进行调用;经常用于处理注释信息。


  • setDebug(dbgFlag=True) - 开启或关闭匹配过程中的跟踪信息


  • validate() - 验证定义的语法是否存在无限递归构造


  • parseWithTabs() - 修改默认将输入字符串的tab转换为空格的行为,很少使用。


  • enablePackrat() - 静态方法,用户开启缓存来提升性能。


2.2 ParserElement类的子类


  • Literal - 构建一个字符串用于精确匹配


  • CaselessLteral - 类似于Literal,不区分大小写,返回的结果是定义的内容,而非输入字符串中的内容。

  • keyword - 类似于Literal,但必须跟随whitespace,标点符号或非关键字字符;阻止非关键字被意外的进行匹配。


  • CaselessKeyword - 类似于keyword,不区分大小写


  • Word - 一个或多个联系的字符;构造的字符串包含一个初始的字符集作为首字符的匹配,和一个可选的字符集进行后续字符串的匹配;例如,在C语言中,一个有效的标识符必须以字母表字符或'_'作为开始,接下来的内容可以包含数字。a,i,MAX_LENGTH,_a1,b_109_,plan9FromOuterSpace都是有效的标识符;而9b7z,$a,.section,0debug却不是。用Word定义一个标识符如下:

    - Word( alphas+"_", alphanums+"_" )
    - Word( srange("[a-zA-Z_]"), srange("[a-zA-Z0-9_]") )

    如果只有一个参数,他将认为第一个字符和后续的字符是一种规则;例如,定义一个只能有字符和'_'的标识符:

    - Word( "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" )
    - Word( srange("[A-Z_]") )



3.2 Expression类的子类


2.4 表达式运算符


2.5 Positional子类


2.6 Converter子类


2.7 特殊的子类


2.8 其他的类


2.9 异常类和问题定位


3 杂项


3.1 辅助方法


3.2 辅助解析动作


3.3 常见的字符串和标记 


你可能感兴趣的:(python,pyparsing)