python 正则表达式使用简介和实用技巧
1. 元字符释义
.
代指任意字符^
从字符串开始匹配$
匹配字符串的结尾*
匹配前面挨着的字符,能匹配0
到无穷次+
同*
,能匹配1
到无穷次(最少1
个)?
匹配前面挨着的字符,匹配0
或1
次{}
自定义匹配次数,{1,6}
匹配1
到6
次,{6}
匹配6
次(重复匹配前面挨着的字符)[]
匹配中括号中的任意字符,[x,y*]
匹配x
或y
或,或*
(括号中无特殊字符),但以下几个除外-
如[a-z]
,匹配a
到z
中的任意字符^
如[^a-z]
,非,匹配不是a
到z
以外的任意字符\
转义
|
或,如'a|b'
匹配'a'
或'b'
()
分组,如'(abc)'
匹配'abc'
(?P<name>\w+)
'?P<name>'
为固定格式,name
为对该组进行命名,'\w'
为真正的匹配规则- 上面的固定格式可不写,主要便于阅读和读取返回结果
\
转义,有以下功能- 将无意义的普通字符变为有意义
\d
匹配任何十进制数,即0-9
\D
匹配任何非数字字符,相当于[^0-9]
\s
匹配任何空白字符,相当于[\t\n\r\f\v]
\S
匹配任何非空白字符,相当于[^\t\n\r\f\v]
\w
对于 Unicode (str) 样式:
匹配Unicode
词语的字符,包含了可以构成词语的绝大部分字符,也包括数字和下划线。如果设置了 ASCII 标志,就只匹配[a-zA-Z0-9_]
。
对于8
位(bytes)样式:
匹配ASCII
字符中的数字和字母和下划线,就是[a-zA-Z0-9_]
。如果设置了 LOCALE 标记,就匹配当前语言区域的数字和字母和下划线。\W
匹配非单词字符的字符。这与\w
正相反。如果使用了 ASCII 旗标,这就等价于[^a-zA-Z0-9_]
。如果使用了 LOCALE 旗标,则会匹配当前区域中既非字母数字也非下划线的字符。\b
匹配一个特殊字符边界,如空格,&
,#
等
- 将有意义的元字符变为无意义
如\. \* \^ \$
等
- 将无意义的普通字符变为有意义
2. 基本使用
查找所有匹配的内容
>>> re.findall(r'\d', '12+(34*6+2-5*(2-1))')
['1', '2', '3', '4', '6', '2', '5', '2', '1']
查找第一个匹配的内容
>>> m = re.search(r'(\d)', '12+(34*6+2-5*(2-1))')
>>> m.groups()
('1',)
使用分组
分组可以在需要其他规则辅助定位,但是又不想获取这些规则所匹配到的内容时使用。
>>> re.findall(r'(\w+)\.', 'abd. efg. 123sd')
['abd', 'efg']
可以使用 ?P<name>
为分组命名,并通过分组名获取内容:
>>> re.search(r'(?P<wgroup>\w+)\.', 'abd. efg. 123sd').groups('wgroup')
('abd',)
迭代返回匹配的结果
如果匹配的内容太多,可以使用该方法优化内容使用。
>>> re.finditer(r'\d', '12+(34*6+2-5*(2-1))')
<callable_iterator at 0x283dbd4bdf0>
从字符串起始位置开始匹配
>>> re.match(r'h', 'hello')
<re.Match object; span=(0, 1), match='h'>
>>> re.match(r'e', 'hello') # None
分割字符串
>>> re.split('ab', 'abcd')
['', 'cd']
用括号将分割的标志包裹起来,可以保留分割的内容:
>>> re.split('(ab)', 'abcd')
['', 'ab', 'cd']
替换字符串
>>> re.sub('[a-z]', '-', 'a123b')
'-123-'
指定最大的替换次数:
>>> re.sub('[a-z]', '-', 'a123b', 1)
'-123b'
同时获取替换的次数
>>> re.subn('[a-z]', '-', 'a123b')
('-123-', 2)
替换时引用前面匹配到的内容
使用命名组:
>>> re.sub('(?P<w>[a-z])', '\g<w>-', 'a123b') # 将 'a' 替换为 'a-', 'b' 替换为 'b-'
'a-123b-'
不使用命名组:
>>> re.sub('([a-z])', '\g<1>-', 'a123b')
'a-123b-'
预编译规则
预编译可以避免每次调用时的编译性能消耗,虽然 re
库也会在内部做缓存,但缓存的数量是有限的,这会导致所传入的表达式不总是能走缓存,使用预编译可以保证只编译一次。
>>> comp = re.compile(r'\d+')
>>> comp.findall('nasa1') # 编译后的对象可以调用 search, findall 等匹配方法
['1']
3. 常用技巧
多行文本匹配时,匹配每一行的内容
>>> text = """
... this is
... a
... multiple
... line
... text
... .
... """
>>> re.findall('^[al]', text, re.M) # 匹配以字母 a, l 开始的行首字母
['a', 'l']
使用多个 flags
如果要同时忽略大小写,以及多行模式匹配,可以使用 |
将多个 flag
拼接起来
>>> text = """
... this is
... A
... multiple
... Line
... text
... .
... """
>>> re.findall('^[al]', text, flags=re.M | re.I)
['A', 'L']
匹配中文
>>> re.findall(r'[\u4e00-\u9fa5]', '你好,我是 john')
['你', '好', '我', '是']
或使用双字节字符匹配(可以包含中文标点符号):
>>> re.findall(r'[^\x00-\xff]', '你好,我是 john')
['你', '好', ',', '我', '是']
匹配不包含某个字符串的的内容
>>> re.findall(r'\b((?!abc)\w+)', 'abcdefg123')
[]
>>> re.findall(r'\b((?!abc)\w+)', 'abdefg123')
['abdefg123']
对上述表达式的解释:
\b
匹配单词的开始或结束?!<exp>
零宽负向先行断言,只会匹配后缀<exp>
不存在的位置\w
匹配字母或数字或下划线或汉字+
重复一次或更多次\b((?!abc)\w)+
匹配由字母或数字或下划线或汉字组成的字串,但字串中不能出现abc
匹配多个连串的字符(去掉分组优先级)
比如,想匹配以 abc
或 123
开始的内容,但是又不想把 abc
或 123
作为一个分组,可以使用 ?:
去掉分组优先级:
>>> re.findall(r'(?P<wgroup>(?:123|abc)\.)', 'abc. efg. 123.')
['abc.', '123.']
其中 (?:123|abc)
使用了分组的语法,但不会被视为一个分组。
4. 其它实例
匹配可有可无的字符
如 http/https 中的 s
>>> pat = re.compile(r'((?:http|https)://.*?/)')
>>> pat.findall('http://www.zhparks.com/upload/')
['http://www.zhparks.com/']
>>> pat.findall('https://www.zhparks.com/upload/')
['https://www.zhparks.com/']
或使用 *
号:
>>> pat = re.compile(r'(https*://.*?/)')
>>> pat.findall('https://www.zhparks.com/upload/')
['https://www.zhparks.com/']
>>> pat.findall('http://www.zhparks.com/upload/')
['http://www.zhparks.com/']