⾃然语⾔处理3.7——⽤正则表达式为⽂本分词
1、分词的简单⽅法:
在空格字符处分割⽂本是⽂本分词最简单的⽅法。考虑⼀下摘⾃《爱丽丝梦游仙境》中的⽂本。
>>> raw = """'When I'M a Duchess,' she said to herself, (not in a very hopeful tone
... though), 'I won't have any pepper in my kitchen AT ALL. Soup does very
... well without--Maybe it's always pepper that makes people hot-tempered,'..."""
可以使⽤raw.split()在空格符处分割原始⽂本。使⽤正则表达式能做同样的事情,匹配字符串中的所有空⽩符是远远不够的,因为这会导致结果中包含'\n'换⾏符。需要同时匹配任何数量的空格符、制表符或者换⾏符。
>>>import re
>>>re.split(r' ',raw)
["'When", "I'M", 'a', "Duchess,'", 'she', 'said', 'to', 'herself,', '(not', 'in',
'a', 'very', 'hopeful', 'tone\nthough),', "'I", "won't", 'have', 'any', 'pepper',
'in', 'my', 'kitchen', 'AT', 'ALL.', 'Soup', 'does', 'very\nwell', 'without--Maybe',
"it's", 'always', 'pepper', 'that', 'makes', 'people', "hot-tempered,'..."]
>>>re.split(r'[ \t\n]+',raw)
["'When", "I'M", 'a', "Duchess,'", 'she', 'said', 'to', 'herself,', '(not', 'in',
'a', 'very', 'hopeful', 'tone', 'though),', "'I", "won't", 'have', 'any', 'pepper',
'in', 'my', 'kitchen', 'AT', 'ALL.', 'Soup', 'does', 'very', 'well', 'without--Maybe',
"it's", 'always', 'pepper', 'that', 'makes', 'people', "hot-tempered,'..."]
正则表达式"[ \t\n]+"匹配⼀个多个空格、制表符或者换⾏符。其他的空⽩字符如回车和换⾏符也应该包含在内。于是re库内置了缩写'\s',表⽰匹配所有的空⽩字符。所有前⾯这个例⼦第⼆条语句可以改写为re.split(r'\s',raw)
在空格符出分割⽂本可得到如"(not"和"herself,"这样的标识符。另⼀种⽅法是使⽤Python提供给我们的
字符类"\w"匹配所有的字符,相当于[0-9a-zA-Z].定义这个类的补充部分"\W"即所有的字母,数字以外的字符。还可以在⼀个简单的正则表达式中⽤\W来分割所有单词以外的输⼊。
>>> re.split(r'\W+', raw)
['', 'When', 'I', 'M', 'a', 'Duchess', 'she', 'said', 'to', 'herself', 'not', 'in',
'a', 'very', 'hopeful', 'tone', 'though', 'I', 'won', 't', 'have', 'any', 'pepper',
'in', 'my', 'kitchen', 'AT', 'ALL', 'Soup', 'does', 'very', 'well', 'without',
'Maybe', 'it', 's', 'always', 'pepper', 'that', 'makes', 'people', 'hot', 'tempered',
'']
可以看到,在开始和结尾处都有⼀个空字符串,通过re.findall(r'\w+',raw)使⽤模式匹配词汇⽽不是空⽩符号,得到相同的标识符。但是没有空字符串。
正则表达式"\w+|\S\w*"会⾸先尝试匹配词中字符的所有序列,如果没有到匹配的,会尝试匹配后⾯跟着词中字符的任何⾮空⽩符。这意味着标点会很跟在后⾯的字母如’s在⼀起,但是两个或两个以上标点字符会被分割。
>>> re.findall(r'\w+|\S\w*', raw)
["'When", 'I', "'M", 'a', 'Duchess', ',', "'", 'she', 'said', 'to', 'herself', ',',
'(not', 'in', 'a', 'very', 'hopeful', 'tone', 'though', ')', ',', "'I", 'won', "'t",
'have', 'any', 'pepper', 'in', 'my', 'kitchen', 'AT', 'ALL', '.', 'Soup', 'does',
'very', 'well', 'without', '-', '-Maybe', 'it', "'s", 'always', 'pepper', 'that',
'makes', 'people', 'hot', '-tempered', ',', "'", '.', '.', '.']
扩展前⾯正则表达式中的“\w+”,允许连字符和撇号“\w+(-')\w+”,他会匹配如hot-dog和it's这样的单词。还需要添加⼀个模式来匹配引号字符使他们与他们包含的⽂字分开。
>>> print(re.findall(r"\w+(?:[-']\w+)*|'|[-.(]+|\S\w*", raw))
["'", 'When', "I'M", 'a', 'Duchess', ',', "'", 'she', 'said', 'to', 'herself', ',',
'(', 'not', 'in', 'a', 'very', 'hopeful', 'tone', 'though', ')', ',', "'", 'I',
"won't", 'have', 'any', 'pepper', 'in', 'my', 'kitchen', 'AT', 'ALL', '.', 'Soup',
'does', 'very', 'well', 'without', '--', 'Maybe', "it's", 'always', 'pepper',
'that', 'makes', 'people', 'hot-tempered', ',', "'", '...']
NLTK的正则表达式分词器
函数p_tokenize()和re.findall()类型,但是p_tokenize()分词效率更⾼,避免了括号的特殊处理的需要。为了增加可读性,将正则表达式分为⼏⾏写,每⼀⾏添加⼀个解释。(?x)‘Verbose’标志告诉Python去掉嵌⼊的注释和空格
>>> text = 'That U.S.A. poster-print costs $'
>>> pattern = r'''(?x)    # set flag to allow verbose regexps
python 正则表达式 空格...    ([A-Z]\.)+        # abbreviations, e.g. U.S.A.
...  | \w+(-\w+)*        # words with optional internal hyphens
...  | \$?\d+(\.\d+)?%?  # currency and percentages, e.g. $12.40, 82%
...  | \.\.\.            # ellipsis
...  | [][.,;"'?():-_`]  # these are separate tokens; includes ], [
... '''
>>> p_tokenize(text, pattern)
['That', 'U.S.A.', 'poster-print', 'costs', '$12.40', '...']
使⽤verbose标志时,可以不使⽤' '来匹配空格字符,⽽是使⽤‘\s’代替。regexp_tokenize()有⼀个可选参数gaps。设置为True时,正则表达式指定标识符之间的距离。就⾏re.split()⼀样。