2.18 字符串令牌解析

    假如你有下面这样一个文本字符串:

    为了令牌化字符串,你不仅需要匹配模式,还得指定模式的类型。比如,你可能想将字符串像下面这样转换为序列对:

    1. tokens = [('NAME', 'foo'), ('EQ','='), ('NUM', '23'), ('PLUS','+'),
    2. ('NUM', '42'), ('TIMES', '*'), ('NUM', '10')]

    为了执行这样的切分,第一步就是像下面这样利用命名捕获组的正则表达式来定义所有可能的令牌,包括空格:

    在上面的模式中, 用于给一个模式命名,供后面使用。

    1. >>> scanner = master_pat.scanner('foo = 42')
    2. >>> scanner.match()
    3. <_sre.SRE_Match object at 0x100677738>
    4. >>> _.lastgroup, _.group()
    5. ('NAME', 'foo')
    6. >>> scanner.match()
    7. <_sre.SRE_Match object at 0x100677738>
    8. >>> _.lastgroup, _.group()
    9. >>> scanner.match()
    10. <_sre.SRE_Match object at 0x100677738>
    11. >>> _.lastgroup, _.group()
    12. ('EQ', '=')
    13. >>> scanner.match()
    14. <_sre.SRE_Match object at 0x100677738>
    15. >>> _.lastgroup, _.group()
    16. ('WS', ' ')
    17. >>> scanner.match()
    18. <_sre.SRE_Match object at 0x100677738>
    19. >>> _.lastgroup, _.group()
    20. ('NUM', '42')
    21. >>> scanner.match()

    实际使用这种技术的时候,可以很容易的像下面这样将上述代码打包到一个生成器中:

    如果你想过滤令牌流,你可以定义更多的生成器函数或者使用一个生成器表达式。比如,下面演示怎样过滤所有的空白令牌:

    1. tokens = (tok for tok in generate_tokens(master_pat, text)
    2. if tok.type != 'WS')
    3. for tok in tokens:
    4. print(tok)

    通常来讲令牌化是很多高级文本解析与处理的第一步。为了使用上面的扫描方法,你需要记住这里一些重要的几点。第一点就是你必须确认你使用正则表达式指定了所有输入中可能出现的文本序列。如果有任何不可匹配的文本出现了,扫描就会直接停止。这也是为什么上面例子中必须指定空白字符令牌的原因。

    令牌的顺序也是有影响的。 re 模块会按照指定好的顺序去做匹配。因此,如果一个模式恰好是另一个更长模式的子字符串,那么你需要确定长模式写在前面。比如:

    最后,你需要留意下子字符串形式的模式。比如,假设你有如下两个模式:

    1. PRINT = r'(?P<PRINT>print)'
    2. NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)'
    3.  
    4. master_pat = re.compile('|'.join([PRINT, NAME]))
    5.  
    6. for tok in generate_tokens(master_pat, 'printer'):
    7. print(tok)
    8.  
    9. # Outputs :
    10. # Token(type='NAME', value='er')

    关于更高阶的令牌化技术,你可能需要查看 或者 PLY 包。一个调用PLY的例子在下一节会有演示。

    原文: