一种用于分词长字符串的替代方法是构建一个单一的万能正则表达式,然后使用命名组来识别标记。这需要一些设置,但识别阶段被推入C/本地代码中,并且只需要一次通过,因此它可以非常高效。例如:
import re
tokens = {
'a': ['andy', 'alpha', 'apple'],
'b': ['baby']
}
def create_macro_re(tokens, flags=0):
"""
Given a dict in which keys are token names and values are lists
of strings that signify the token, return a macro re that encodes
the entire set of tokens.
"""
d = {}
for token, vals in tokens.items():
d[token] = '(?P<{}>{})'.format(token, '|'.join(vals))
combined = '|'.join(d.values())
return re.compile(combined, flags)
def find_tokens(macro_re, s):
"""
Given a macro re constructed by `create_macro_re()` and a string,
return a list of tuples giving the token name and actual string matched
against the token.
"""
found = []
for match in re.finditer(macro_re, s):
found.append([(t, v) for t, v in match.groupdict().items() if v is not None][0])
return found
最后一步,运行它:
macro_pat = create_macro_re(tokens, re.I)
print find_tokens(macro_pat, 'this is a string of baby apple Andy')
macro_pat
最终对应于:
re.compile(r'(?P<a>andy|alpha|apple)|(?P<b>baby)', re.IGNORECASE)
第二行输出一个元组列表,每个元组都包含标记和与标记匹配的实际字符串:
[('b', 'baby'), ('a', 'apple'), ('a', 'Andy')]
这个例子展示了如何将令牌列表编译成单个正则表达式,并可以在一次扫描中对字符串进行高效运行。
其中一个伟大的优势没有显示出来:不仅可以通过字符串定义令牌,还可以通过正则表达式定义。因此,如果我们想要
b标记的替代拼写,例如,我们不必详尽列出它们。普通的正则表达式模式就足够了。假设我们还想将'babby'识别为
b标记。我们可以像以前一样使用
'b': ['baby', 'babby']
,也可以使用正则表达式来完成相同的事情:
'b': ['babb?y']
。或者如果您想包括任意内部的'b'字符,则使用
'bab+y'
。