Python使用re模块实现okenizer(表达式分词器)

 更新时间:2022年04月30日 14:10:04   作者:orion-orion  
这篇文章主要介绍了Python使用re模块实现okenizer,我们这里讲解用正则表达式构建简单的表达式分词器(tokenizer),它能够将表达式字符串从左到右解析为标记(tokens)流,需要的朋友可以参考下

一个简单的tokenizer

分词(tokenization)任务是Python字符串处理中最为常见任务了。我们这里讲解用正则表达式构建简单的表达式分词器(tokenizer),它能够将表达式字符串从左到右解析为标记(tokens)流。

给定如下的表达式字符串:

text = 'foo = 12 + 5 * 6'

我们想要将其转换为下列以序列对呈现的分词结果:

tokens = [('NAME', 'foo'), ('EQ', '='), ('NUM', '12'), ('PLUS', '+'),\
    ('NUM', '5'), ('TIMES', '*'), ('NUM', '6')]

要完成这样的分词操作,我们首先需要定义出所有可能的标记模式(所谓模式(pattern),为用来描述或者匹配/系列匹配某个句法规则的字符串,这里我们用正则表达式来做为模式),注意此处要包括空格whitespace,否则字符串中出现任何模式中没有的字符后,扫描就会停止。因为我们还需要给标记以NAME、EQ等名称,我们采用正则表达式中的命名捕获组来实现。

import re
NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)' 
# 这里?P<NAME>表示模式名称,()表示一个正则表达式捕获组,合在一起即一个命名捕获组
EQ = r'(?P<EQ>=)'
NUM = r'(?P<NUM>\d+)' #\d表示匹配数字,+表示任意数量
PLUS = r'(?P<PLUS>\+)' #需要用\转义
TIMES = r'(?P<TIMES>\*)' #需要用\转义
WS = r'(?P<WS>\s+)' #\s表示匹配空格, +表示任意数量
master_pat = re.compile("|".join([NAME, EQ, NUM, PLUS, TIMES, WS]))  # | 用于选择多个模式,表示"或"

接下来我们用模式对象中的scanner()方法来完成分词操作,该方法创建一个扫描对象:

scanner = master_pat.scanner(text)

然后可以用match()方法获取单次匹配结果,一次匹配一个模式:

scanner = master_pat.scanner(text)
m = scanner.match() 
print(m.lastgroup, m.group()) # NAME foo
m = scanner.match()
print(m.lastgroup, m.group()) # WS

当然这样一次一次调用过于麻烦,我们可以使用迭代器来批量调用,并将单次迭代结果以具名元组形式存储

Token = namedtuple('Token', ['type', 'value'])
def generate_tokens(pat, text):
    scanner = pat.scanner(text)
    for m in iter(scanner.match, None):
        #scanner.match做为迭代器每次调用的方法,
        #None为哨兵的默认值,表示迭代到None停止
        yield Token(m.lastgroup, m.group())
for tok in generate_tokens(master_pat, "foo = 42"):
    print(tok)

最终显示表达式串"foo = 12 + 5 * 6"的tokens流为:

Token(type='NAME', value='foo')
Token(type='WS', value=' ')
Token(type='EQ', value='=')
Token(type='WS', value=' ')
Token(type='NUM', value='12')
Token(type='WS', value=' ')
Token(type='PLUS', value='+')
Token(type='WS', value=' ')
Token(type='NUM', value='5')
Token(type='WS', value=' ')
Token(type='TIMES', value='*')
Token(type='WS', value=' ')
Token(type='NUM', value='6')

过滤tokens流

接下来我们想要过滤掉空格标记,使用生成器表达式即可:

tokens = (tok for tok in generate_tokens(master_pat, "foo = 12 + 5 * 6")
          if tok.type != 'WS')
for tok in tokens:
    print(tok)

可以看到空格被成功过滤:

Token(type='NAME', value='foo')
Token(type='EQ', value='=')
Token(type='NUM', value='12')
Token(type='PLUS', value='+')
Token(type='NUM', value='5')
Token(type='TIMES', value='*')
Token(type='NUM', value='6')

注意子串匹配陷阱

tokens在正则表达式(即"|".join([NAME, EQ, NUM, PLUS, TIMES, WS]))中顺序也非常重要。因为在进行匹配时,re模块就会按照指定的顺序对模式做匹配。故若碰巧某个模式是另一个较长模式的子串时,必须保证较长的模式在前面优先匹配。如下面分别展示正确的和错误的匹配方法:

LT = r'(?P<LT><)'
LE = r'(?P<LE><=)'
EQ = r'(?P<EQ>>=)'
master_pat = re.compile("|".join([LE, LT, EQ]))  # 正确的顺序
master_pat = re.compile("|".join([LT, LE, EQ]))  # 错误的顺序

第二种顺序的错误之处在于,这样会把'<='文本匹配为LT('<')紧跟着EQ('='),而没有匹配为单独的LE(<=)。

我们对于“有可能”形成子串的模式也要小心,比如下面这样:

PRINT = r'(?P<PRINT>print)'
NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)'
master_pat = re.compile("|".join([PRINT, NAME]))  # 正确的顺序
for tok in generate_tokens(master_pat, "printer"):
    print(tok)

可以看到被print实际上成了另一个模式的子串,导致另一个模式的匹配出现了问题:

# Token(type='PRINT', value='print')
# Token(type='NAME', value='er')

更高级的语法分词,建议采用像PyParsing或PLY这样的包。特别地,对于英文自然语言文章的分词,一般被集成到各类NLP的包中(一般分为按空格拆分、处理前后缀、去掉停用词三步骤)。对于中文自然语言处理分词也有丰富的工具(比如jieba分词工具包)。

引用

[1] Martelli A, Ravenscroft A, Ascher D. Python cookbook[M]. " O'Reilly Media, Inc.", 2015. 数学是符号的艺术,音乐是上界的语言。

到此这篇关于Python使用re模块实现okenizer的文章就介绍到这了,更多相关Python okenizer内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python实现协程的具体示例

    python实现协程的具体示例

    协程是一种轻量级的并发编程技术,它允许程序在某个点上暂停执行,本文主要介绍了python实现协程的具体示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Python常用字符串替换函数strip、replace及sub用法示例

    Python常用字符串替换函数strip、replace及sub用法示例

    这篇文章主要介绍了Python常用字符串替换函数strip、replace及sub用法,结合实例形式分析了Python针对字符串替换的常用函数strip、replace及sub功能及简单使用技巧,需要的朋友可以参考下
    2018-05-05
  • 使用Python如何将视频按照一定时间切割(比如:每10s进行裁切)

    使用Python如何将视频按照一定时间切割(比如:每10s进行裁切)

    这篇文章主要介绍了使用Python将视频按照一定时间切割(比如:每10s进行裁切),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • Python中reduce()函数的语法参数与作用详解

    Python中reduce()函数的语法参数与作用详解

    这篇文章主要介绍了Python中reduce()函数的语法参数与作用详解,reduce函数是通过函数对迭代器对象中的元素进行遍历操作,Python3.x中reduce函数已经从内置函数中取消了,转而放在functools模块中,需要的朋友可以参考下
    2023-08-08
  • Python解析m3u8拼接下载mp4视频文件的示例代码

    Python解析m3u8拼接下载mp4视频文件的示例代码

    这篇文章主要介绍了Python解析m3u8拼接下载mp4视频文件的示例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • 详解Pandas 处理缺失值指令大全

    详解Pandas 处理缺失值指令大全

    这篇文章主要介绍了详解Pandas 处理缺失值指令大全,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • python处理文本文件并生成指定格式的文件

    python处理文本文件并生成指定格式的文件

    本节主要介绍了python如何处理文本文件并生成指定格式的文件,需要的朋友可以参考下
    2014-07-07
  • Python打印酷炫日志的方法详解

    Python打印酷炫日志的方法详解

    在Python中,日志是一种非常重要的工具,可以帮助我们更好地了解程序的运行情况,本文将介绍如何使用logging模块来打印炫酷的日志,需要的可以参考一下
    2023-06-06
  • Python入门篇之数字

    Python入门篇之数字

    本文的主题是 Python 中的数字。会详细介绍每一种数字类型,它们适用的各种运算符, 以及用于处理数字的内建函数。在文章的末尾, 简单介绍了几个标准库中用于处理数字的模块。
    2014-10-10
  • 利用Python+Excel制作一个视频下载器

    利用Python+Excel制作一个视频下载器

    说起Excel,那绝对是数据处理领域王者般的存在。而作为网红语言Python,在数据领域也是被广泛使用。本文将利用Python和Excel制作一个视频下载器,需要的可以参考一下
    2022-05-05

最新评论