9参考书籍
《Python Cookbook》
需要将一个字符串分割成几个字段,但是整个字符串中的分隔符和周围的空格不一致。
In [82]: line='asdf fjdk; afed, fjek,asdf, foo' In [83]: line.split(";") Out[83]: ['asdf fjdk', ' afed, fjek,asdf, foo'] In [84]: line.split(";",",") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) /home/caribbean/<ipython console> in <module>() TypeError: an integer is required
字符串对象的split()函数不能处理多个分隔符,也不能去除掉分隔符周围的空白,这种情况下可以使用re.split()函数
In [123]: line='asdf fjdk; afed, fjek,asdf, foo' In [124]: re.split(r'[;,\s]',line) Out[124]: ['asdf', 'fjdk', '', 'afed', '', 'fjek', 'asdf', '', '', '', '', '', 'foo'] In [125]: re.split(r'[;,\s]\s*',line) Out[125]: ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
\s 表示匹配任意空白字符 等价于 [ \t\n\r\f\v].
* 表示匹配前面正则表达式的0次或多次
当使用re.split()函数时,需要注意的是匹配的正则表达式是否需要包含以圆括号包围的捕获组(capture group),如果使用了捕获组,那么匹配的文本也会被显示出来。
In [126]: re.split(r'(;|,|\s)\s*',line) Out[126]: ['asdf', ' ', 'fjdk', ';', 'afed', ',', 'fjek', ',', 'asdf', ',', 'foo']
In [138]: fields=re.split(r'(;|,|\s)\s*',line) In [139]: fields Out[139]: ['asdf', ' ', 'fjdk', ';', 'afed', ',', 'fjek', ',', 'asdf', ',', 'foo'] In [140]: values=fields[::2] In [141]: delimiters=fields[1::2] + [''] In [142]: delimiters Out[142]: [' ', ';', ',', ',', ',', ''] In [143]: values Out[143]: ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo'] In [165]: ''.join(v+d for v,d in zip(values,delimiters) ) Out[166]: 'asdf fjdk;afed,fjek,asdf,foo'
如果不想分隔字符出现在结果中,但是仍然想用圆括号将部分正则表达式分组,可以使用nocapture group。
In [169]: re.split(r'(?:,|;|\s)\s*',line) Out[169]: ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
2.需要检查一个字符串的开头或结尾是否包含指定的文本内容
简单的检查一个字符串的开头或结尾可以使用startswith()和endswith()
In [176]: filename='spam.txt' In [177]: filename.endswith('.txt') Out[177]: True In [178]: filename.startswith('file:') Out[178]: False In [179]: filename.startswith('spam') Out[179]: True In [180]: url='http://www.python.org' In [181]: url.startswith('http') Out[181]: True
如果有多个选项需要匹配,需要给startswith() 或 endswith()传输一个元组
In [3]: choices=['http:','ftp:'] In [4]: url='http://www.python.org' In [5]: url.startswith(choices) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-5-618fcafc4cd7> in <module>() ----> 1 url.startswith(choices) TypeError: expected a character buffer object In [6]: url.startswith(tuple(choices)) Out[6]: True
3.使用shell通配符匹配字符串
如果想使用shell通配符匹配字符串,可以使用fnmatch模块
In [20]: from fnmatch import fnmatch,fnmatchcase In [21]: fnmatch('foo.txt','*.txt') Out[21]: True
默认情况下fnmatch()随着操作系统区分大小写,如果要特别区分大小写可以使用fnmatchcase()
4.匹配并搜索文本模式
如果只是简单匹配一些文本字符,可以使用str.find(),str.endswith(),str.startswith()等这些基本的函数,匹配复杂的模式就要用到正则表达式和re模块
In [1]: text='yeah, but no, but yeah, but no, but yeah' In [2]: text=='yeah' Out[2]: False In [3]: text.startswith('yeah') Out[3]: True In [4]: text.endswith('yeah') Out[4]: True In [5]: text.endswith('no') Out[5]: False In [6]: text.find('no') Out[6]: 10
匹配一个字符串中是否含有类似 11/27/2012这样的日期格式
In [15]: text1='11/27/2012' In [16]: text2='Nov 27,2012' In [17]: import re In [18]: if re.match(r'\d+/\d+/\d+',text1): ....: print('yes') ....: else: ....: print('no') ....: ....: yes In [19]: if re.match(r'\d+/\d+/\d+',text2): ....: print('yes') ....: else: ....: print('no') ....: ....: no
\d+ 表示匹配一个或多个数字
如果对同一个匹配模式要匹配多次,通常可以将这个正则表达式匹配模式预编译成一个匹配模式对象。
In [22]: datepat=re.compile(r'\d+/\d+/\d+') In [23]: if datepat.match(text1): ....: print('yes') ....: else: ....: print('no') ....: ....: yes
match()总是从一个字符串的开头去匹配,如果匹配,就不再继续查找,如果想要在一个字符中搜索匹配模式所有出现的位置,就需要使用findall()函数
In [60]: text='Today is 11/27/2012.PyCon starts 3/13/2013.' In [61]: datepat.findall(text) Out[61]: ['11/27/2012', '3/13/2013']
使用正则表达式的时候,通常使用捕获组将部分匹配模式扩住
In [172]: datepat=re.compile(r'(\d+)/(\d+)/(\d+)') In [173]: m=datepat.match('11/27/2012') In [174]: m Out[174]: <_sre.SRE_Match object at 0x2b02918> In [175]: m.group(0) Out[175]: '11/27/2012' In [176]: m.group(1) Out[176]: '11' In [177]: m.group(2) Out[177]: '27' In [178]: m.group(3) Out[178]: '2012' In [179]: m.groups() Out[179]: ('11', '27', '2012')
findall()搜索文本找到所有的匹配项,返回一个元组,如果想迭代的找到匹配的字符串,可以使用finditer()函数
In [195]: for m in datepat.finditer(text): .....: print(m.groups()) .....: .....: ('11', '27', '2012') ('3', '13', '2013')
在指定匹配模式时,通常会使用原始字符串例如 r'(\d+)/(\d+)/(\d+)' ,这种字符不会解析类似反斜杠\这样的字符,如果不使用原始字符的话,就要写成'(\\d+)/(\\d+)/(\\d+)'这种形式.
如果只是仅仅作简单的文本匹配或搜索,可以跳过编译步骤,直接使用re模块自带的模块层函数。
In [201]: re.findall(r'(\d+)/(\d+)/(\d+)',text) Out[201]: [('11', '27', '2012'), ('3', '13', '2013')]
5.搜索并替换文本
简单的文本替换可以使用str.replace()函数,复杂一点的可以使用re模块中的sub()函数
In [232]: text='yeah, but no,but yeah, but no, but yeah' In [233]: text.replace('yeah','yep') Out[233]: 'yep, but no,but yep, but no, but yep'
In [242]: text='Today is 11/27/2012. PyCon starts 3/13/2013.' In [243]: import re In [244]: re.sub(r'(\d+)/(\d+)/(\d+)',r'\3-\1-\2',text) Out[244]: 'Today is 2012-11-27. PyCon starts 2013-3-13.'
\3代表捕获组序号,这里是2012
如果要执行多次匹配同一个匹配模式的操作,可以先将匹配模式编译一下再执行
In [242]: text='Today is 11/27/2012. PyCon starts 3/13/2013.' In [243]: import re In [244]: re.sub(r'(\d+)/(\d+)/(\d+)',r'\3-\1-\2',text) Out[244]: 'Today is 2012-11-27. PyCon starts 2013-3-13.'
如果想要知道替换的次数,可以使用re.subn()函数
In [372]: newtext,n=datepat.subn(r'\3-\1-\2',text) In [373]: newtext Out[373]: 'Today is 2012-11-27. PyCon starts 2013-3-13.' In [374]: n Out[374]: 2