1.3.11 利用模式拆分
str.split()是分解字符串来完成解析的最常用的方法之一。不过,它只支持使用字面量只作为分隔符。有时,如果输入没有一致的格式,那么就需要有一个正则表达式。例如,很多纯文本标记语言都把段落分隔符定义为两个或多个换行符(\n)。在这种情况下,就不能使用str.split(),因为这个定义中提到了“或多个”。通过findall()标识段落的一种策略是使用类似(.+?)\n{2,}的模式。
import re
text = '''Paragraph one
on two lines.
Paragraph two.
Paragraph three.'''
for num,para in enumerate(re.findall(r'(.+?)\n{2,}',text,flags=re.DOTALL)):
print(num,repr(para))
print()
对于输入文本末尾的段落,这个模式会失败,原因在于“Paragraph three.”不是输出的一部分。
运行结果:
可以扩展这个模式,指出段落以两个或多个换行符结束或者以输入末尾结束,这就能修正这个问题,但也会让模式变得更为复杂。可以转而使用re.split()而非re.findall(),这便能自动地处理边界条件,并保证模式更简单。
import re
text = '''Paragraph one
on two lines.
paragraph two.
Paragraph three.'''
print('With findall:')
for num,para in enumerate(re.findall(r'(.+?)(\n{2,}|$)',text,
flags=re.DOTALL)):
print(num,repr(para))
print()
print()
print('With split:')
for num,para in enumerate(re.split(r'\n{2,}',text)):
print(num,repr(para))
print()
split()的模式参数更准确地表述了标记规范。由两个或多个换行符标记输入字符串中段落之间的分隔点。
运行结果:
可以将表达式包围在括号里来定义一个组,这使得split()的工作更类似于str.partition(),因此它会返回分隔符值以及字符串的其他部分。
import re
text = '''Paragraph one
on two lines.
Paragraph two.
Paragraph three.'''
print('With split:')
for num,para in enumerate(re.split(r'(\n{2,})',text)):
print(num,repr(para))
print()
现在输出包括各个段落,以及分隔这些段落的换行符序列。
运行结果: