1 问题
你希望查找并替换一段文本中的模式串。
2. 解决方案
如果以简单的字符串字面量形式给出模式串,那么你通常可以使用基本的字符串方法如 str.replace()
,例如:
>>> text = 'yeah, but no, but yeah, but no, but yeah'
>>> text.replace('yeah', 'yep')
'yep, but no, but yep, but no, but yep'
对于更加复杂的模式串,则需要结合 re
模块的 sub()
函数/方法以及正则表达式。为了演示,假设你希望将形如 11/27/2012
的日期重写为 2012-11-27
形式。例如:
>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
>>> import re
>>> re.sub(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)
'Today is 2012-11-27. PyCon starts 2013-3-13.'
在 re.sub()
函数中,第一个参数用于文本匹配的正则表达式,第二个参数是用于进行替换的文本模式,其中的反斜杠加数字如 \3
指的是第一个参数中的捕捉分组编号。
如果你希望使用某个正则表达式进行多次重复的文本匹配,那么可以考虑提前对其进行编译以提高执行的速度。例如:
>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
>>> import re
>>> date_pattern = re.compile(r'(\d+)/(\d+)/(\d+)')
>>> date_pattern.sub(r'\3-\1-\2', text)
'Today is 2012-11-27. PyCon starts 2013-3-13.'
对于复杂的文本替换,还可以考虑定义一个用于文本替换的回调函数。例如:
>>> from calendar import month_abbr
>>> def change_date(m):
... mon_name = month_abbr[int(m.group(1))]
... return '{} {} {}'.format(m.group(2), mon_name, m.group(3))
...
>>> re.sub(r'(\d+)/(\d+)/(\d+)', change_date, text)
'Today is 27 Nov 2012. PyCon starts 13 Mar 2013.'
>>> date_pattern.sub(change_date, text)
'Today is 27 Nov 2012. PyCon starts 13 Mar 2013.'
如上所述,作为输入,传入替换回调函数的实参是类似 match()
和 find()
以及 search()
等函数返回的匹配对象。在回调函数中通过调用匹配对象的 group()
方法以剥离出想要的部分。
如果你除了想获取替换后的文本之外,还希望知道发生了多少次替换,那么可以使用 re.subn()
函数,例如:
>>> text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'
>>> re.subn(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)
('Today is 2012-11-27. PyCon starts 2013-3-13.', 2)
>>> text_with_replacement, num = re.subn(r'(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)
>>> text_with_replacement
'Today is 2012-11-27. PyCon starts 2013-3-13.'
>>> num
2
3. 讨论
对于使用正则表达式进行文本的搜索和替换,基本上都逃不过上述 re.sub()
函数的用法。实际上,最有挑战性是其实是写出正确的正则表达式。