python正则表达式
正则表达式并不是python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。
得益于这一点,再提供正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言所支持的语法数量不一样。
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符串比较,如果每一个字符都能匹配成功,则匹配成功,一旦有匹配不成功的字符串则匹配失败。
python支持的正则表达式元字符和语法
字符 | 说明 | 匹配对象 | 匹配结果 |
一般字符 | 匹配自身 | abc | abc |
. | 除换行符之外的任何字符 | a.c | abc/acc/a2c |
转义字符 | 转义字符,使后一个字符改变原有的意思。例如匹配*,可以\* | a\*c | a*c |
[] |
字符集。匹配字符集中的任意一个字符。可以缩写, 比如[0-9]、[A-Z],还有一些特殊的字符,比如说[^...],表示除...之外 [*]匹配*这个字符,在字符集中特殊字符都失去了原有的含义。 |
a[1-9e-g]c | a3c/agc |
字符 | 说明 | 匹配对象 | 匹配结果 |
\s | 空白字符 | a\sc | a c |
\S | 非空白字符 | a\Sc | a1c 或abc |
\d | 数字字符 |
a\dc |
a2c |
\D | 非数字字符 | a\Dc | adc |
\w | 数字、字母、下划线 | a\wc | a_c |
\W | 非数字字母下划线 | a\Wc | a c |
\n | 换行符 |
|
|
\t | 制表符 |
|
字符 | 说明 | 匹配对象 | 匹配结果 |
* | 匹配0或多个任意字符 | 李.* | 李/李洋/李杰傻... |
? | 匹配0或1个任意字符 | 李.? | 李/李杰 |
+ | 匹配1或多个任意字符 | 李.+ | 李洋/李杰傻... |
{m} | 匹配m个 | 李{3} | 李李李 |
{m,}/{,n} | 匹配0-n或者大于m个 | 李{2,} | 李李李/李李李李/李李李李李.. |
{m,n} | 匹配m到n个字符 | 李{3,5} | 李李李/李李李李/李李李李李.. |
字符 | 说明 | 匹配对象 | 匹配结果 |
^ | 以什么开头 | ||
$ | 以什么结尾 | ||
\A | 仅匹配字符串开头 | ||
\Z | 仅匹配字符串结尾 |
re模块中常用的方法
(1)findall:找到所有符合的对象,返回一个列表
>>> re.findall('o','Hello,world') ['o', 'o'] >>> re.findall('l','Hello,world') ['l', 'l', 'l']
findall还有一个需要注意——分组优先
>>> re.findall(r'www\.(baidu|oldboy)\.com',r'www.oldboy.com') ['oldboy'] >>> re.findall(r'www\.(baidu|oldboy)\.com',r'www.baidu.com') ['baidu'] #并没有像我们预期的那样,在findall中,括号括起来优先级更高 #怎么解决了? >>> re.findall(r'www\.(?:baidu|oldboy)\.com',r'www.baidu.com') ['www.baidu.com']
(2)search:找到第一个,返回值用group()打印
>>> re.search('l','Hello,world') <_sre.SRE_Match object; span=(2, 3), match='l'> >>> ret = re.search('l','Hello,world') >>> ret.group() #使用group()打印 'l'
search不会出现findall优先级的问题,但我们又想只想显示一部分,该如何解决了?
#可以给它传一个参数 >>> re.search(r'www\.(?P<web_name>baidu|oldboy)\.com',r'www.baidu.com').group('web_name') 'baidu' >>> re.search(r'www\.(?P<web_name>baidu|oldboy)\.com',r'www.oldboy.com').group('web_name') 'oldboy'
一些小的技巧:
>>> re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group() '<h1>hello</h1>' #可以省略一个位置 #也可以使用编号 >>> re.search(r"<(\w+)>\w+</\1>",r"<h1>hello</h1>").group() '<h1>hello</h1>' #按照组的位置定序 >>> re.search(r"<(\w+)>\w+</\1>",r"<h1>hello</h2>").group() Traceback (most recent call last): #前后不一样,报错 File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'group'
如果不给组起名字,也可以使用\序号来找到对应的组,表示要找的内容和前面的内容一致。
(3)match:从开始匹配,返回值用group()打印
>>> ret1 = re.match('l','Hello,world') #必须匹配的是首字符 >>> ret1.group() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'group' >>> ret1 = re.match('H','Hello,world') >>> ret1.group() 'H'
还有一个关于匹配对象和组的知识点,也拿出来一起看看
group():获取给定组的匹配项
start():返回给定组的匹配项开始的位置
end():返回给定组的匹配项结束的位置
span():返回一个组开始和结束的位置
#有小括号框起来的我们称之为组,每个组都有默认的位置 >>> re.match(r'www\.(.*)\..{3}','www.python.org').group(1) 'python' >>> re.match(r'www\.(.*)\..{3}','www.python.org').group() 'www.python.org' >>> >>> re.match(r'www\.(.*)\..{3}','www.python.org').start() 0 >>> re.match(r'www\.(.*)\..{3}','www.python.org').end() 14 >>> re.match(r'www\.(.*)\..{3}','www.python.org').span() (0, 14) >>> re.match(r'www\.(.*)\.(.{3})','www.python.org').start(1) 4 >>> re.match(r'www\.(.*)\.(.{3})','www.python.org').end(1) 10 >>> re.match(r'www\.(.*)\.(.{3})','www.python.org').span(1) (4, 10) >>> re.match(r'www\.(.*)\.(.{3})','www.python.org').start(2) 11 >>> re.match(r'www\.(.*)\.(.{3})','www.python.org').end(2) 14 >>> re.match(r'www\.(.*)\.(.{3})','www.python.org').span(2) (11, 14)
(4)split:根据正则表达式的方法分割
>>> ret2 = re.split('[ac]','abcacd') >>> ret2 ['', 'b', '', '', 'd'] #以a分割,返回''和'bcacd #分割bcacd,以c分割,反回一个b #分割acd,以a分割,返回一个空'' #分割cd,以c分割,返回一个空'',剩余一个d不能分割
用正则表达式切分字符串 比用固定的字符更加灵活。
>>> re.split('\s','a b c') ['a', 'b', '', 'c'] >>> re.split('\s+','a b c') #必须加一个”+“ ['a', 'b', 'c'] >>> re.split('[\s,]','a, b, c') ['a', '', 'b', '', '', 'c'] >>> re.split('[\s,]+','a, b, c') #有加号就可以匹配多个空格。 ['a', 'b', 'c']
有时候又想保留分割符,该如何做了?
#确实有小技巧 >>> re.split('\d','kebi25maoxian24xiaoniao18') ['kebi', '', 'maoxian', '', 'xiaoniao', '', ''] >>> re.split('\d+','kebi25maoxian24xiaoniao18') ['kebi', 'maoxian', 'xiaoniao', ''] #并没有把数字留下 >>> re.split('(\d+)','kebi25maoxian24xiaoniao18') ['kebi', '25', 'maoxian', '24', 'xiaoniao', '18', ''] #加一个()就可以 >>> ret = re.split('(\d+)','kebi25maoxian24xiaoniao18') >>> ret.remove('') #删除最后一个空元素 >>> ret ['kebi', '25', 'maoxian', '24', 'xiaoniao', '18']
当然,你还可以指定分割的次数。
>>> ret ['kebi', '25', 'maoxian', '24', 'xiaoniao18'] >>> re.split('&',"毛线&科比&科比老婆") ['毛线', '科比', '科比老婆'] #如果科比和他的老婆不想分开了? >>> re.split('&',"毛线&科比&科比老婆",1) ['毛线', '科比&科比老婆']
(5)sub/subn:根据正则表达式的方法替换
>>> re.sub('\d','NUM','my old is 56') 'my old is NUMNUM' >>> re.subn('\d','NUM','my old is 56') ('my old is NUMNUM', 2)
(6)compile:编译正则
将正则表达式转换为模式对象,可以实现更有效率的匹配。
如果在调用search和match函数的时候使用字符串表示的正则表达式,它们会在内部将字符串转换为正则表达式对象。
>>> obj = re.compile('123') >>> ret4 = re.search(obj,'abc123456cda') >>> ret4.group() '123'
(7)finditer:返回一个迭代器
>>> ret5 = re.finditer('l','Hello,world') >>> '__next__' in dir(ret5) True
(8)escape:
re.escape可以对字符串中所有可能被解释为正则运算符的字符进行转义的应用函数。
如果字符串很长且包含很多特殊字符,而你又不想输入一大堆反斜线,或者字符串来自于用户(通过input交互式输入),且要用做正则表达式的一部分的时候,可以使用这个函数。
>>> re.escape('www.baicu.com') 'www\\.baicu\\.com' >>> re.escape('I am Kobe, a 25 year old young man') 'I\\ am\\ Kobe\\,\\ a\\ 25\\ year\\ old\\ young\\ man'