字符串令牌解析

问题

你有一个字符串,想从左至右将其解析为一个令牌流。

解法

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

text = 'foo = 23 + 42 * 10'

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

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

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

import re
NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)'
NUM = r'(?P<NUM>\d+)'
PLUS = r'(?P<PLUS>\+)'
TIMES = r'(?P<TIMES>\*)'
EQ = r'(?P<EQ>=)'
WS = r'(?P<WS>\s+)'
master_pat = re.compile('j'.join([NAME, NUM, PLUS, TIMES, EQ, WS]))

在上面的模式中, ?P 用于给一个模式命名,供后面使用。下一步,为了令牌化,使用模式对象很少被人知道的 scanner() 方法。这个方法会创建一个 scanner 对象,在这个对象上不断的调用 match() 方法会一步步的扫描目标文本,每步一个匹配。下面是演示一个 scanner 对象如何工作的交互式例子:

>>> scanner = master_pat.scanner('foo = 42')
>>> scanner.match()
<_sre.SRE_Match object at 0x100677738>
>>> _.lastgroup, _.group()
('NAME', 'foo')
>>> scanner.match()
<_sre.SRE_Match object at 0x100677738>
>>> _.lastgroup, _.group()
('WS', ' ')
>>> scanner.match()
<_sre.SRE_Match object at 0x100677738>
>>> _.lastgroup, _.group()
('EQ', '=')
>>> scanner.match()
<_sre.SRE_Match object at 0x100677738>
>>> _.lastgroup, _.group()
('WS', ' ')
>>> scanner.match()
<_sre.SRE_Match object at 0x100677738>
>>> _.lastgroup, _.group()
('NUM', '42')
>>> scanner.match()
>>>

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

def generate_tokens(pat, text):Token = namedtuple('Token', ['type', 'value'])scanner = pat.scanner(text)for m in iter(scanner.match, None):yield Token(m.lastgroup, m.group())# Example use
for tok in generate_tokens(master_pat, 'foo = 42'):print(tok)
# Produces output
# Token(type='NAME', value='foo')
# Token(type='WS', value=' ')
# Token(type='EQ', value='=')
# Token(type='WS', value=' ')
# Token(type='NUM', value='42')

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

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

讨论

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

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

LT = r'(?P<LT><)'
LE = r'(?P<LE><=)'
EQ = r'(?P<EQ>=)'
master_pat = re.compile('j'.join([LE, LT, EQ])) # Correct
# master_pat = re.compile('j'.join([LT, LE, EQ])) # Incorrect

第二个模式是错的,因为它会将文本<= 匹配为令牌 LT 紧跟着 EQ,而不是单独的令牌 LE,这个并不是我们想要的结果。

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

PRINT = r'(?P<PRINT>print)'
NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)'
master_pat = re.compile('j'.join([PRINT, NAME]))
for tok in generate_tokens(master_pat, 'printer'):
print(tok)
# Outputs :
# Token(type='PRINT', value='print')
# Token(type='NAME', value='er')

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

《Python Cookbook 3rd》笔记(2.18):字符串令牌解析相关推荐

  1. python cookbook 读书笔记2(字符串处理2)

    1.8检查字符集中是否包含某字符集中的字符,在以下几个例子中,也就是检查seq是否包含aset中的项,各函数中Any结尾是部分包含,也就是只要有其中元素就返回true,All结尾的是全部包含,必须包含 ...

  2. 《Python Cookbook 3rd》笔记汇总

    文章目录 一.数据结构 二.字符串和文本 三.数字.日期和时间 四.迭代器与生成器 五.文件与IO 一.数据结构 标题 关键词 1.1:拆分序列后赋值给多个变量 可迭代对象.拆分赋值 1.2:拆分任意 ...

  3. 《Python Cookbook 3rd》笔记(1.4):查找最大或最小的N个元素

    <Python Cookbook 3rd>1.4:查找最大或最小的N个元素 问题 怎样从一个集合中获得最大或者最小的N个元素列表? 解法 heapq 模块有两个函数:nlargest()和 ...

  4. Python Cookbook 3rd Edition Documentation

    Python Cookbook 3rd Edition Documentation 文章目录 第一章:数据结构和算法 1.1 解压序列赋值给多个变量 问题 解决方案 讨论 1.2 解压可迭代对象赋值给 ...

  5. 《Python cookbook》笔记二

    <Python cookbook>笔记二 第二章 字符串和文本 -使用多个界定符分割字符串- 你需要将一个字符串分割为多个字段,但是分隔符 (还有周围的空格) 并不是固定 的. # str ...

  6. Machine Learning with Python Cookbook 学习笔记 第6章

    Chapter 6. Handling Text 本笔记是针对人工智能典型算法的课程中Machine Learning with Python Cookbook的学习笔记 学习的实战代码都放在代码压缩 ...

  7. Machine Learning with Python Cookbook 学习笔记 第8章

    Chapter 8. Handling Images 前言 本笔记是针对人工智能典型算法的课程中Machine Learning with Python Cookbook的学习笔记 学习的实战代码都放 ...

  8. Machine Learning with Python Cookbook 学习笔记 第9章

    Chapter 9. Dimensionality Reduction Using Feature Extraction 前言 本笔记是针对人工智能典型算法的课程中Machine Learning w ...

  9. 《Python Cookbook 3rd》笔记(2.19):实现一个简单的递归下降分析器

    实现一个简单的递归下降分析器 问题 你想根据一组语法规则解析文本并执行命令,或者构造一个代表输入的抽象语法树.如果语法非常简单,你可以自己写这个解析器,而不是使用一些框架. 解法 在这个问题中,我们集 ...

最新文章

  1. php中怎样表示组合框,php – 如何实现动态组合框选择系统
  2. 使用Maven程序集创建漏洞评估工件
  3. bandwidth 0.32k 发布,内存带宽测试工具
  4. POJ1061 青蛙的约会【扩展欧几里得算法】
  5. pytorch使用说明2
  6. 原生js的dom操作
  7. Windows和Linux平台上实现Word转PDF
  8. STM32 使用串口下载程序( ISP 一键下载)
  9. 规则引擎drools教程一
  10. 使用无人机倾斜摄影测量技术采集某县城区地理信息数据并生成实景三维模型的案例
  11. 光纤与PON基础概念整理
  12. 华为HCIP-DATACOM(821)411-440
  13. 【python逻辑算法题】一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法
  14. MT4电脑版软件下载前后有哪些注意事项?
  15. 高精度定位系统精细测距定位
  16. 四川铺管家:拼多多产品布局怎么做
  17. java Lambda表达式练习
  18. 极客日报第 53 期:抖音将代替拼多多成为春晚独家红包合作伙伴;高通正研发 8cx 升级版处理器,对标苹果 M1;DuckDuckGo 日查询量首次突破 1 亿
  19. 【云周刊】第135期:云栖大会珍贵技术资料:20+覆盖容器技术、智能工业、大数据、开源数据库...
  20. 办公室面积增大,实现wifi覆盖的解决方案

热门文章

  1. ARM中断分析之一:中断控制器和CPU、外设的关系
  2. 哪些设计模式最值得学习
  3. 蜂窝注册表和永久存储
  4. 密码锁 java接口_从synchronized和lock区别入手聊聊java锁机制
  5. 【转】VScode快捷键(超无敌详细版)
  6. 第九节:基于MVC5+AutoFac+EF+Log4Net的基础结构搭建
  7. Qt实现多屏幕多分辨率自适应
  8. matlab连接mysql教程视频_Matlab建立到Oracle数据库的连接
  9. CCNA-第一篇-基础入门概念
  10. 【POJ - 1511】 Invitation Cards(Dijkstra + 反向建图 多源到单源最短路的处理)