W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
你有一個(gè)字符串,想從左至右將其解析為一個(gè)令牌流。
假如你有下面這樣一個(gè)文本字符串:
text = 'foo = 23 + 42 * 10'
為了令牌化字符串,你不僅需要匹配模式,還得指定模式的類型。比如,你可能想將字符串像下面這樣轉(zhuǎn)換為序列對(duì):
tokens = [('NAME', 'foo'), ('EQ','='), ('NUM', '23'), ('PLUS','+'),
('NUM', '42'), ('TIMES', '*'), ('NUM', 10')]
為了執(zhí)行這樣的切分,第一步就是像下面這樣利用命名捕獲組的正則表達(dá)式來定義所有可能的令牌,包括空格:
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('|'.join([NAME, NUM, PLUS, TIMES, EQ, WS]))
在上面的模式中,?P<TOKENNAME>
用于給一個(gè)模式命名,供后面使用。
下一步,為了令牌化,使用模式對(duì)象很少被人知道的 scanner()
方法。這個(gè)方法會(huì)創(chuàng)建一個(gè) scanner
對(duì)象,在這個(gè)對(duì)象上不斷的調(diào)用 match()
方法會(huì)一步步的掃描目標(biāo)文本,每步一個(gè)匹配。下面是演示一個(gè) scanner
對(duì)象如何工作的交互式例子:
>>> 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()
>>>
實(shí)際使用這種技術(shù)的時(shí)候,可以很容易的像下面這樣將上述代碼打包到一個(gè)生成器中:
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')
如果你想過濾令牌流,你可以定義更多的生成器函數(shù)或者使用一個(gè)生成器表達(dá)式。比如,下面演示怎樣過濾所有的空白令牌:
tokens = (tok for tok in generate_tokens(master_pat, text)
if tok.type != 'WS')
for tok in tokens:
print(tok)
通常來講令牌化是很多高級(jí)文本解析與處理的第一步。為了使用上面的掃描方法,你需要記住這里一些重要的幾點(diǎn)。第一點(diǎn)就是你必須確認(rèn)你使用正則表達(dá)式指定了所有輸入中可能出現(xiàn)的文本序列。如果有任何不可匹配的文本出現(xiàn)了,掃描就會(huì)直接停止。這也是為什么上面例子中必須指定空白字符令牌的原因。
令牌的順序也是有影響的。re模塊會(huì)按照指定好的順序去做匹配。因此,如果一個(gè)模式恰好是另一個(gè)更長(zhǎng)模式的子字符串,那么你需要確定長(zhǎng)模式寫在前面。比如:
LT = r'(?P<LT><)'
LE = r'(?P<LE><=)'
EQ = r'(?P<EQ>=)'
master_pat = re.compile('|'.join([LE, LT, EQ])) # Correct
# master_pat = re.compile('|'.join([LT, LE, EQ])) # Incorrect
第二個(gè)模式是錯(cuò)的,因?yàn)樗鼤?huì)將文本<=匹配為令牌LT緊跟著EQ,而不是單獨(dú)的令牌LE,這個(gè)并不是我們想要的結(jié)果。
最后,你需要留意下子字符串形式的模式。比如,假設(shè)你有如下兩個(gè)模式:
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)
# Outputs :
# Token(type='PRINT', value='print')
# Token(type='NAME', value='er')
關(guān)于更高階的令牌化技術(shù),你可能需要查看 PyParsing 或者 PLY 包。一個(gè)調(diào)用PLY的例子在下一節(jié)會(huì)有演示。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: