網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Python使用re模塊實(shí)現(xiàn)okenizer(表達(dá)式分詞器)_python
作者:orion-orion ? 更新時(shí)間: 2022-06-27 編程語(yǔ)言一個(gè)簡(jiǎn)單的tokenizer
分詞(tokenization)任務(wù)是Python字符串處理中最為常見(jiàn)任務(wù)了。我們這里講解用正則表達(dá)式構(gòu)建簡(jiǎn)單的表達(dá)式分詞器(tokenizer),它能夠?qū)⒈磉_(dá)式字符串從左到右解析為標(biāo)記(tokens)流。
給定如下的表達(dá)式字符串:
text = 'foo = 12 + 5 * 6'
我們想要將其轉(zhuǎn)換為下列以序列對(duì)呈現(xiàn)的分詞結(jié)果:
tokens = [('NAME', 'foo'), ('EQ', '='), ('NUM', '12'), ('PLUS', '+'),\ ('NUM', '5'), ('TIMES', '*'), ('NUM', '6')]
要完成這樣的分詞操作,我們首先需要定義出所有可能的標(biāo)記模式(所謂模式(pattern),為用來(lái)描述或者匹配/系列匹配某個(gè)句法規(guī)則的字符串,這里我們用正則表達(dá)式來(lái)做為模式),注意此處要包括空格whitespace,否則字符串中出現(xiàn)任何模式中沒(méi)有的字符后,掃描就會(huì)停止。因?yàn)槲覀冞€需要給標(biāo)記以NAME、EQ等名稱(chēng),我們采用正則表達(dá)式中的命名捕獲組來(lái)實(shí)現(xiàn)。
import re NAME = r'(?P<NAME>[a-zA-Z_][a-zA-Z_0-9]*)' # 這里?P<NAME>表示模式名稱(chēng),()表示一個(gè)正則表達(dá)式捕獲組,合在一起即一個(gè)命名捕獲組 EQ = r'(?P<EQ>=)' NUM = r'(?P<NUM>\d+)' #\d表示匹配數(shù)字,+表示任意數(shù)量 PLUS = r'(?P<PLUS>\+)' #需要用\轉(zhuǎn)義 TIMES = r'(?P<TIMES>\*)' #需要用\轉(zhuǎn)義 WS = r'(?P<WS>\s+)' #\s表示匹配空格, +表示任意數(shù)量 master_pat = re.compile("|".join([NAME, EQ, NUM, PLUS, TIMES, WS])) # | 用于選擇多個(gè)模式,表示"或"
接下來(lái)我們用模式對(duì)象中的scanner()
方法來(lái)完成分詞操作,該方法創(chuàng)建一個(gè)掃描對(duì)象:
scanner = master_pat.scanner(text)
然后可以用match()
方法獲取單次匹配結(jié)果,一次匹配一個(gè)模式:
scanner = master_pat.scanner(text) m = scanner.match() print(m.lastgroup, m.group()) # NAME foo m = scanner.match() print(m.lastgroup, m.group()) # WS
當(dāng)然這樣一次一次調(diào)用過(guò)于麻煩,我們可以使用迭代器來(lái)批量調(diào)用,并將單次迭代結(jié)果以具名元組形式存儲(chǔ)
Token = namedtuple('Token', ['type', 'value']) def generate_tokens(pat, text): scanner = pat.scanner(text) for m in iter(scanner.match, None): #scanner.match做為迭代器每次調(diào)用的方法, #None為哨兵的默認(rèn)值,表示迭代到None停止 yield Token(m.lastgroup, m.group()) for tok in generate_tokens(master_pat, "foo = 42"): print(tok)
最終顯示表達(dá)式串"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')
過(guò)濾tokens流
接下來(lái)我們想要過(guò)濾掉空格標(biāo)記,使用生成器表達(dá)式即可:
tokens = (tok for tok in generate_tokens(master_pat, "foo = 12 + 5 * 6") if tok.type != 'WS') for tok in tokens: print(tok)
可以看到空格被成功過(guò)濾:
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在正則表達(dá)式(即"|".join([NAME, EQ, NUM, PLUS, TIMES, WS])
)中順序也非常重要。因?yàn)樵谶M(jìn)行匹配時(shí),re
模塊就會(huì)按照指定的順序?qū)δJ阶銎ヅ?。故若碰巧某個(gè)模式是另一個(gè)較長(zhǎng)模式的子串時(shí),必須保證較長(zhǎng)的模式在前面優(yōu)先匹配。如下面分別展示正確的和錯(cuò)誤的匹配方法:
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])) # 錯(cuò)誤的順序
第二種順序的錯(cuò)誤之處在于,這樣會(huì)把'<='
文本匹配為L(zhǎng)T('<'
)緊跟著EQ('='
),而沒(méi)有匹配為單獨(dú)的LE(<=
)。
我們對(duì)于“有可能”形成子串的模式也要小心,比如下面這樣:
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
實(shí)際上成了另一個(gè)模式的子串,導(dǎo)致另一個(gè)模式的匹配出現(xiàn)了問(wèn)題:
# Token(type='PRINT', value='print') # Token(type='NAME', value='er')
更高級(jí)的語(yǔ)法分詞,建議采用像PyParsing或PLY這樣的包。特別地,對(duì)于英文自然語(yǔ)言文章的分詞,一般被集成到各類(lèi)NLP的包中(一般分為按空格拆分、處理前后綴、去掉停用詞三步驟)。對(duì)于中文自然語(yǔ)言處理分詞也有豐富的工具(比如jieba
分詞工具包)。
引用
[1] Martelli A, Ravenscroft A, Ascher D. Python cookbook[M]. " O'Reilly Media, Inc.", 2015. 數(shù)學(xué)是符號(hào)的藝術(shù),音樂(lè)是上界的語(yǔ)言。
原文鏈接:https://www.cnblogs.com/orion-orion/p/16206925.html
相關(guān)推薦
- 2022-11-06 Python命令行參數(shù)解析包argparse的使用詳解_python
- 2022-01-20 出現(xiàn)SLF4J: Failed to load class “org.slf4j.impl.Stat
- 2022-05-15 在CentOS7上搭建本地GitLab服務(wù)器_Linux
- 2022-04-26 ASP.NET?Core?MVC中Required與BindRequired用法與區(qū)別介紹_基礎(chǔ)應(yīng)用
- 2022-08-16 C#工程建立后修改工程文件名與命名空間操作_C#教程
- 2022-07-13 nginx-1.20*安裝check模塊
- 2023-12-10 記錄兩個(gè)Excel導(dǎo)出出現(xiàn)的問(wèn)題
- 2023-01-13 碼云(gitee)通過(guò)git自動(dòng)同步到阿里云服務(wù)器_服務(wù)器其它
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支