官术网_书友最值得收藏!

2.7 令牌解析

下面介紹將字符串從左至右解析為令牌流。

文本字符串示例如下:


text_v = 'foo = 23 + 42 * 10'

令牌化字符串不僅需要匹配模式,還得指定模式的類型。如將字符串轉換為序列對,示例如下:


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

為了執行上述切分,第一步就是利用命名捕獲組的正則表達式來定義所有可能的令牌,包括空格,代碼(token_parser.py)如下:


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>用于給定一個模式命名,供后面的程序使用。

為了使字符串令牌化,我們使用模式對象中的scanner()方法。該方法會創建一個Scanner對象,在這個對象上不斷地調用match()方法掃描目標文本,相關代碼(token_parser.py)示例如下:


scanner = master_pat.scanner('foo = 42')
match_val = scanner.match()
print(match_val)
print(match_val.lastgroup, match_val.group())

match_val = scanner.match()
print(match_val)
print(match_val.lastgroup, match_val.group())

match_val = scanner.match()
print(match_val)
print(match_val.lastgroup, match_val.group())

match_val = scanner.match()
print(match_val)
print(match_val.lastgroup, match_val.group())

match_val = scanner.match()
print(match_val)
print(match_val.lastgroup, match_val.group())

match_val = scanner.match()
print(match_val)

執行py文件,輸出結果如下:


<re.Match object; span=(0, 3), match='foo'>
NAME foo
<re.Match object; span=(3, 4), match=' '>
WS  
<re.Match object; span=(4, 5), match='='>
EQ =
<re.Match object; span=(5, 6), match=' '>
WS  
<re.Match object; span=(6, 8), match='42'>
NUM 42
None

實際使用這種技術的時候,我們可以很容易地將上述代碼打包到一個生成器中,相關代碼(token_parser.py)示例如下:


from collections import namedtuple
def generate_tokens(pat, text):
    Token = namedtuple('Token', ['type', 'value'])
    pat_scanner = pat.scanner(text)
    for m in iter(pat_scanner.match, None):
        yield Token(m.lastgroup, m.group())

for tok in generate_tokens(master_pat, 'foo = 42'):
    print(tok)

執行py文件,輸出結果如下:


Token(type='NAME', value='foo')
Token(type='WS', value=' ')
Token(type='EQ', value='=')
Token(type='WS', value=' ')
Token(type='NUM', value='42')

如果想過濾令牌流,可以定義更多的生成器函數或者使用一個生成器表達式,以下示例展示了過濾所有的空白令牌:


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

通常來講,令牌化是很多高級文本解析與處理的第一步。

對于上述掃描方法,我們需要記住一點:必須確認使用正則表達式指定所有輸入中可能出現的文本序列。如果有任何不可匹配的文本出現,掃描會直接停止。這也是上面示例中必須指定空白字符令牌的原因。

令牌的順序對解析結果也是有影響的。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)

主站蜘蛛池模板: 都安| 五寨县| 庆云县| 水城县| 肃北| 乌拉特中旗| 平谷区| 南汇区| 鞍山市| 建平县| 徐州市| 长治县| 迭部县| 祁连县| 宣汉县| 绥宁县| 桐梓县| 浠水县| 广昌县| 怀安县| 锦屏县| 东乌珠穆沁旗| 塔城市| 会理县| 邵武市| 都安| 上林县| 四子王旗| 邢台市| 常熟市| 麻栗坡县| 枞阳县| 贵州省| 平泉县| 临汾市| 宜丰县| 甘谷县| 扎兰屯市| 察隅县| 荥经县| 阜新市|