regex 正则表达式
前言
**动机 : **
- 处理文本成为计算机主要工作之一
- 根据文本内容进行固定搜索是文本处理的常见工作
- 为了快速方便的处理上述问题,正则表达式技术诞生,逐渐发展为一个单独技术被众多语言使用
**定义 : **
即高级文本匹配模式,提供了搜索,替代等功能,本质是由一些字符和特殊符号组成的字串。
这个字串描述了字符和字符的重复行为,可以匹配某一类特征的字符串集合。
正则特点:
- 方便进行检索和修改
- 支持语言众多
- 使用灵活变化多样
- 文本处理,mongo存储某一类型字串,django、tornado路由,爬虫文本匹配
简单示例:
import re # 标准模块
re.findall(regex,string)
功能 :
使用正则表达式匹配字符串
参数:
regex 正则表达式
string 目标字符串
返回值 : 匹配到的内容
元字符
即正则表达式中有特殊含义的字符,列表如下:
abc 等普通字符
元字符 : abc
匹配规则 : 匹配相应的普通字符
e.g. ab ----》 abcdef : ab
示例:
In [3]: re.findall('ab','abcdefabcde')
Out[3]: ['ab', 'ab']
| 符号两侧的正则均能匹配
e.g. ab|cd —》 abcdefgh : ab cd
示例:
re.findall('ab|fg','abcdefgabcde') # 写成 re.findall('ab | fg', 'xxxx') 就完全不一样了,含空格
# ['ab', 'fg', 'ab']
补充:
错误的使用 “ab | cd” 将空格也包含进去了。匹配结果为空;
. 匹配任意一个字符 '\n’除外
e.g. f.o —》 foo fuo fao f@o
In [7]: re.findall('f.o','affooasand f@o,')
Out[7]: ['ffo', 'f@o'] # 注意,‘foo'就没有匹配出来,这是函数 findall() 规定的;
^ 匹配字符串开头
匹配规则: 匹配一个字符串的开头位置
e.g. ^Hello —> Hello world : Hello
In [13]: re.findall('^from', 'from the city.')
Out[13]: ['from']
In [31]: re.findall('^from', 'I come from the city.') # from 非字符串首,匹配到空;
Out[31]: []
$ 匹配字符串结尾
匹配规则: 匹配一个字符串的结尾位置
e.g. py$ ----> hello.py : py
In [10]: re.findall('py$','hello.py')
Out[10]: ['py']
In [16]: re.findall('py$', 'python') # 未匹配到,因为不是 'py' 结尾;
Out[16]: []
* 匹配重复0次或多次
元字符: * (常用)
匹配规则:匹配前面出现的正则表达式 0次或者多次
e.g. ab* 匹配这些 a ab abbbb…
In [26]: re.findall('.*py$', 'hello.py')
Out[26]: ['hello.py']
+ 匹配重复1次或多次
匹配规则: 匹配前面正则表达式至少一次
e.g ab+ ab abbbbb
In [29]: re.findall('.+\.py$','a.py') # ’\.‘ 为转义;匹配*.py 文件名
Out[29]: ['a.py']
? 匹配重复0次或1次
匹配规则 : 匹配前面出现的正则表达式0次或1次
e.g. ab? a ab
In [34]: re.findall('ab?','abcdeabasdfabbbbbb')
Out[34]: ['ab', 'ab', 'a', 'ab'] # 贪婪模式(默认):明明可以只匹配'a',但是结果是'ab';
{N} 匹配重复指定次数
元字符 : {N}
匹配规则 : 匹配前面的正则表达式N次
e.g. ab{3} abbb
{M,N} 匹配重复指定次数范围
元字符 : {M,N}
匹配规则 : 匹配前面的正则表达式 m次到n次,范围 [m, n] 都是闭区间;
e.g ab{3,5} abbb abbbb abbbbb
In [39]: re.findall('ab{2,5}','abbcdeabbbabsdfabbbbbb')
Out[39]: ['abb', 'abbb', 'abbbbb']
[abcd] 字符集匹配
元字符 : [abcd]
匹配规则 : 匹配中括号中的字符集,或者是字符集区间 的一个字符
[abcd] ----》 a b c d
[0-9] ---> 1 3 4 7 匹配任意一个数字字符
[A-Z] ---》 A D H 匹配任意一个大写字符
[a-z] ---》 a d f h 匹配任意一个小写字符
[+-*/0-9a-g] --》 + - * / 4 b
注:多个字符集形式可以写在一起
In [42]: re.findall('^[A-Z][0-9a-z]{5}','Hello1 Join')
Out[42]: ['Hello1']
In [42]: re.findall('^[A-Z][0-9a-z]+','Hello1daxqd Join')
Out[42]: ['Hello1daxqd']
[^ …] 字符集不匹配
元字符 : [^ …]
匹配规则 : 匹配出字符集中字符的任意一个字符
e.g.
[^abcd] -> e f & #
[^0-9] -> a d g
In [48]: re.findall('[^_0-9a-zA-Z]','[email protected]') # 匹配任意特殊符号;
Out[48]: ['@', '.']
\d 匹配任意数字字符
\D 匹配任意非数字字符
元字符 : \d 等价于 [0-9] , \D 等价于 [^0-9]
匹配规则: \d 匹配任意一个数字字符 \D 匹配任意非数字字符
In [49]: re.findall('1\d{10}','15100317766')
Out[49]: ['15100317766']
\w 匹配任意普通字符
\W 匹配任意特殊字符
元字符 : \w [_0-9a-zA-Z] \W [^_0-9a-zA-Z]
匹配规则 : \w 匹配数字字母下划线 \W 除了(数字字母下划线)
In [20]: re.findall('[A-Z]\w*', 'Hello World')
Out[20]: ['Hello', 'World']
In [21]: re.findall('\w+-\d+', 'wangming-26') # 格式,'姓名-年龄'
Out[21]: ['wangming-26']
\s 匹配任意空字符
\S 匹配任意 非空字符
元字符 : \s \S
匹配规则: \s 任意空字符 [ \n\0\t\r] 空格 换行 回车 制表
\S 任意非空字符
In [61]: re.findall('hello\s+\S+','hello l&#l hello lucy hellokadfh')
Out[61]: ['hello l&#l', 'hello lucy']
^ 匹配字符串开头位置
$ 匹配字符串的结尾位置
\A 相当于’^’
\Z 相当于’$’
- 匹配字符串开头结尾
元字符 : \A ^ \Z $
匹配规则: \A 表示匹配字符串开头位置 #相当于 ”^”
\Z 表示匹配字符串的结尾位置 #相当于 “¥“
e.g. \Aabc\Z ---> abc # 严格匹配 'abc'
\b 匹配单词边界
\B 匹配非单词边界
元字符: \b \B
匹配规则 \b 匹配一个单词的边界
\B 匹配一个单词的非边界
数字字母下划线和其他字符的交界处认为是 【单词边界】
is “This is a test”
In [71]: re.findall(r'\bis\b','This is a test') # 注意 r'\bis\b' 中的 r ;
Out[71]: ['is']
元字符总结
字符: 匹配实际字符
匹配单个字符 : . \d \D \w \W \s \S [...] [^...]
匹配重复次数 : * + ? {N} {M,N}
匹配字串位置 : ^ $ \A \Z \b \B
其他 : |
r"字串" 和 转义
转义
需要转义的字符: . * ? $ “” ‘’ [] () {} \
r —> 将字符串变为 raw 原始字串,不进行字符串的转义;(重要)
两种等价的写法
In [87]: re.findall(r'\? \* \\','what? * \\')
Out[87]: ['? * \\']
# 这是最正规的写法,但是太麻烦了,一般选择传 原始字符串给第一个参数;
In [88]: re.findall('\\? \\* \\\\','what? * \\')
Out[88]: ['? * \\']
注意:
1. findall() 函数的两个参数都是字符串,第一个参数字符串用于生成 正则表达式 字符串,
第二个参数,用于生成待匹配的字符串;
2. 至于为什么前面可以直接使用 re.findall(‘hello\s+\S+\d\w+’,‘xxxx’),是因为没有\s等的转义字符,python 自动作了处理,
生成相应的正则表达式;
3. 若想偷懒,可以在两字符串参数前都加 ‘r’ ,这样就简单多了。
贪婪和非贪婪
和重复元字符相关
-
- ? {m,n}
贪婪模式
在使用重复元字符的时候(* + ? {m,n}),元字符的匹配总是尽可能多的向后匹配更多内容,即为贪婪模式。
贪婪模式是一种默认情况
e.g. 尽可能多的匹配b
In [2]: re.findall('ab*', 'abbbbbalkjsfab')
Out[2]: ['abbbbb', 'a', 'ab']
In [90]: re.findall('ab+','abbbbbbalksdjfab')
Out[90]: ['abbbbbb', 'ab']
非贪婪模式
尽可能少的匹配内容,只要满足正则条件即可
贪婪 —》 非贪婪 *? +? ?? {m,n}? # “你这么贪,好吗?”, 多问了一下,想了一下,就不贪了。(可以这么理解)
尽量少匹配
In [95]: re.findall('ab*?', 'abbbbbbalksdjfab')
Out[95]: ['a', 'a', 'a']
In [96]: re.findall('ab+?','abbbbbbalksdjfab')
Out[96]: ['ab', 'ab']
正则表达式的分组
使用()为正则表达式分组 # 同C编程的正则,() 表示子匹配串;
注:
关于自组,就不用 re.findall() 函数演示了,因为它只显示子组的内容;
接下来使用 re.match()来演示;
((ab)cd(ef)) : 表示给ab分了一个子组
-
正则表达式的子组用()表示,增加子组后对整体的匹配没有影响
-
每个正则表达式可以有多个子组,子组由外到内、由左到右为第一第二第三。。。。。。子组
((ab)cd(ef))
第一子组:(abcdef)
第二子组:(ab)
第三子组:(ef) -
子组表示一个内部整体,很多函数可以单独提取子组的值
In [108]: re.match(’(ab)cdef’,‘abcdefghig’).group(1) # group(1) 单独提取子组内容;
Out[108]: ‘ab’
In [30]: re.match(’(ab)cdef’,‘abcdefghig’).group(0) # 参数只能去 0~1,取2就报异常;
Out[30]: ‘abcdef’ -
子组可以改变 重复行为,将子组作为一个整体重复
In [111]: re.match('(ab)*','ababababab').group()
Out[111]: 'ababababab'
捕获组和非捕获组 (命名组和非命名组)
格式 : (?Pregex)
示例:
(ab)cdef --》 (?Pab)cdef
作用:
- 某些函数可以通过名字提取子组内容,或者通过名字进行键值对的生成。
In [112]: re.match('(?P<word>ab)cdef','abcdefghi').group()
Out[112]: 'abcdef'
- 起了名字的子组可以通过名称重复使用
(?P=name)
示例:
(ab)cdef(ab) --》 (?Pab)cdef(?P=word)
In [114]: re.match('(?P<word>ab)cd(?P=word)','abcdabdfewf').group()
Out[114]: 'abcdab'
练习 :
匹配长度为8-10位的密码。必须以字母开头,数字字母下划线组成
1\w{7,9}$
匹配身份证号
\d{17}(\d|x) # 最后一位是数字或x
re模块
compile(pattern, flags=0)
功能 : 获取正则表达式对象
参数 : pattern 正则表达式
flags 功能标志位 提供正则表达式结果的辅助功能
返回值:返回相应的正则对象
In [15]: obj = re.compile('abc')
In [16]: help(obj.findall) # 可以比较 re 模块的 findall() 方法;
In [17]: dir(obj) # 查看对象属性和方法;
compile对象属性:
groupindex : compile对象属性 得到捕获组名和第几组数字组成的字典
groups: compile 对象属性,得到一共有多少子组
示例见 regex.py
- compile 函数返回对象的属性函数 和 re模块属性函数有相同的部分
相同点:
*功能完全相同
不同点:
compile返回值对象属性函数参数中没有pattern和flags部分,因为这两个参数内容在compile生成对象时已经指明,
而re模块直接调用这些函数时则需要传入
compile返回值对象属性函数参数中有pos 和 endpos参数,可以指明匹配目标字符串的起始位置,
而re模块直接调用这些函数时是没有这个
findall(string,pos=0, endpos=9223372036854775807) #compile对象 方法
功能 : 将正则表达式匹配到的内容存入一个列表返回
参数 : 要匹配的目标字符串
返回值: 返回匹配到的内容列表
- 如果正则表达式中有子组,则返回子组的匹配内容,同样也是一个列表;
示例:
In [18]: pattern = r’(ab)cd(ef)’
In [19]: obj = re.compile(pattern)
In [20]: L = obj.findall(‘abcdefghig’)
In [21]: L
Out[21]: [(‘ab’, ‘ef’)]
In [22]: obj.match(‘abcdefghig’).group() # 显示全部
Out[22]: ‘abcdef’
In [23]: obj.match(‘abcdefghig’).group(1) # 第一子组 group(1)
Out[23]: ‘ab’
In [24]: obj.match(‘abcdefghig’).group(2) # 第二子组 group(2)
Out[24]: ‘ef’
finditer(string, pos=0, endpos=9223372036854775807)
功能 : 同findall 查找所有正则匹配到的内容
参数:同findall
返回值 : 返回一个迭代器,迭代的每一项都是一个match 对象
示例:
In [38]: obj = re.compile(r'foo')
In [39]: itt = obj.finditer('foo, food on the table.')
In [40]: for i in itt:
...: print(i.group())
foo
foo
split(pattern, string, maxsplit=0, flags=0)
split(string, maxsplit=0)
功能: 以正则表达式切割字符串
返回值 : 分割后的内容放入列表
示例:
In [25]: re.split(r’\s+’, ‘hello world nihao china’)
Out[25]: [‘hello’, ‘world’, ‘nihao’, ‘china’]
sub(repl, string, count=0)
sub(pattern,re_string,string,count=0, flags=0) # 源字符串不变,返回一个新的字符串;
功能:用目标字符串替换正则表达式匹配内容
参数:re_string 用什么来替换
string 要匹配的目标字符串
count 最多替换几处
返回值 : 返回替换后的字符串
示例:
In [28]: re.sub(r’[A-Z]’, ‘##’, ‘Hi, Jame. It is a sunny day.’, 2)
Out[28]: ‘##i, ##ame. It is a sunny day.’
subn(pattern, repl, string, count=0, flags=0)
subn(repl, string, count=0)
功能 : 同sub
参数 : 同sub
返回值 : 比sub多一个实际替换的个数
示例:
In [30]: re.subn(r’[A-Z]’, ‘##’, ‘Hi, Jame. It is a sunny day.’, 2)
Out[30]: (’##i, ##ame. It is a sunny day.’, 2)
In [31]: re.subn(r’[A-Z]’, ‘##’, ‘Hi, Jame. It is a sunny day.’)
Out[31]: (’##i, ##ame. ##t is a sunny day.’, 3) # 实际替换3处;
match(pattern, string, flags=0)
match(string, pos=0, endpos=9223372036854775807)
功能:匹配一个字符串开头的位置
参数:
pattern 正则表达式
string 目标字符串 (字符串开头)
返回值 : 如果匹配到返回 一个match obj
没匹配到返回None,抛出异常 AttributeError.
search(pattern, string, flags=0)
search(string, pos=0, endpos=9223372036854775807)
功能:同match 只是可以匹配任意位置,只能匹配一处
参数:目标字符串
返回值 : 如果匹配到返回 一个match obj
没匹配到返回None
fullmatch(pattern, string, flags=0)
fullmatch(string, pos=0, endpos=9223372036854775807)
要求目标字符串能够被正则表达式完全匹配
In [12]: obj = re.fullmatch(’\w+’,‘abcd2’)
In [13]: obj.group()
Out[13]: ‘abcd2’
=================================================================
match 对象属性及函数
‘end’
‘endpos’
‘group’
‘groupdict’
‘groups’
‘lastgroup’
‘lastindex’
‘pos’
‘re’,
‘span’
‘start’
group(n=0)
功能:获取match 对象匹配到的内容
参数:默认为0,表示获取整体匹配
groupdict()
flags: re 直接调用的匹配函数大多有 flags 参数,功能为辅助正则匹配的标志位;
I, IGNORECASE 匹配时,忽略大小写;
S, DOTALL 匹配换行,对 . 元字符起作用;
M, MULTILINE 开头结尾计算换行,对^$起作用;(对\A \Z 不起作用)
X, VERBOSE 让正则能以#添加注释,还必须换行;(基本不用)
同时添加多个flags
如 re.S | re.I
a-zA-Z ↩︎