实用Python程序设计MOOC-第九章正则表达式
[TOC]
实用Python程序设计MOOC-第九章正则表达式
正则表达式
可以用来判断某个字符串是否符合某种模式,比如判断某个字符串是不是邮箱地址,网址,电话号码,身份证号….
可以用来在文本中寻找并抽取符合某种模式的字符串,比如电子邮件地址,电话号码,网址,身份证号…找出三国演义中所有孔明提到曹操的场合都说了些啥。
正则表达式的基本概念和构成
正则表达式是个某些字符有特殊含义字符串,表示一种字符串的模式(格式),如:
“abc” 匹配 “abc”
“b.?p. * k” 匹配 “bapk”, “bpabk”
“\d{3} [a-zA-Z]+. (\d{2} |N/A) \s\1” 匹配????
可以用相关函数求给定字符串和正则表达式的匹配情况
正则表达式中的功能字符
字符/组合 | 匹配的模式 | 正则表达式 | 匹配的字符串 | |||
---|---|---|---|---|---|---|
. |
除'\n' 外的任意一个字符,包括汉字(多行匹配方式下也能匹配'\n' ) |
'a.b' |
‘acb’,’adb’,’a(b’,… | |||
* |
量词。表示左边的字符可出现0次或任意多次 | 'a*b' |
‘b’,’ab’,’aaaab’,… | |||
? |
量词。表示左边的字符必须出现0次或1次 | 'ka?b' |
‘kb’,’kab’ | |||
+ |
量词。表示左边的字符必须出现1次或更多次 | 'ka+b' |
‘kab’,’kaaab’,… | |||
{m} |
量词。m是整数。表示左边的字符必须且只能出现m次 | 'ka{3}a' |
‘kaaaa’ | |||
{m,n} |
量词。m,n是整数。表示左边的字符必须出现至少m次,最多n次。n也可以不写,表示出现次数没有上限 | 'ka{1,3}b' |
‘kab’,’kaab’,’kaaab’ | |||
\d |
一个数字字符, 等价于[0-9] | 'a\db' |
‘a2b’,’a3b’,… | |||
\D |
一个非数字的字符,等价于[^\d],[^0-9] |
'a\Db' |
‘acb’,… | |||
\s |
一个空白字符,如空格、\t 、\r 、\n 等 |
'a\sb' |
‘a b’,’a\nb’,… | |||
\S |
一个非空白字符 | 'a\Sb' |
‘akb’,… | |||
\w |
一个单词字符:包括汉字或大小写英文字母,数字,下划线,或其它语言的文字 | 'a\wb' |
‘a_b’,’a中b’,… | |||
\W |
个不是单词字符的字符 | 'a\Wb' |
‘a?b’,… | |||
` | ` | ` A | B `表示能匹配A或能匹配B均算能匹配 | `’ab | c’` | ‘ab’,’c’ |
1 | print("\\s\s\S\w\W\d\D") #python字符串中,\s等不是转移字符,都是两个字符 |
正则表达式中的特殊字符
正则表达式中常见的特殊字符有以下几个:
+ ? * $ [] () ^ {} \
如果要在正则表达式中表示这几个字符本身,就应该在其前面加'\'
。
字符/组合 | 匹配的模式 |
---|---|
'a\$b' |
'a$b' |
'a\*b' |
'a*b' |
'a\[\]b' |
'a[]b' |
'a\.*b' |
'ab' ,'a.b' ,'a..b' ,… |
'a\\\\b' |
'a\\b' (注意:此字符串长度为3,中间那个字符串是'\' ,即r'a\b)' |
r'a\\b' |
r'a\b' (r'a\b' 等价于'a\\b' ) |
1 | print("\*\$\.\+\[\]\(\)\?\^\{\}") #这些都不是转移字符,都是两个字符 |
字符范围和量词
[XXX]的用法
用以表示”此处必须出现一个某某范围内的字符”,或者”此处必须出现一个字符,但不可以是某某范围内的字符”
字符/组合 | 匹配的模式 | 正则表达式 | 匹配的字符串 |
---|---|---|---|
[a2c] | 匹配’a’,’2’,’c’之一 | ‘s[a2c]k’ | ‘sak’,’s2k’,’sck’ |
[a-zA-Z] | 匹配任一英文字母 | ‘b[a-zA-Z]k’ | ‘bak’,’bUk’,… |
[\da-z\?] | 匹配一个数字或小写英文字母或’?’ | ‘b[\da-z\?]k’ | ‘b0k’,’bck’,’b?k’,… |
[^abc] |
匹配一个非’a’,’b’,’c’之一的字符 | 'b[^abc]k' |
匹配所有能匹配’b.k’的字符串,除了:’bak’,’bbk’,’bck’ |
[^a-f0-3] |
匹配一个非英文字母’a’到’f’,也非数字’0’到’3’的的字符 | 略 | 略 |
匹配汉字
汉字的unicode编码范围是4e00-9fa5 (16进制) ,因此[\u4e00-\u9fa5]
即表示一个汉字
1 | print('\u4e00\u4e01\u4e88\u9fa5') #>>一丁予顧 |
量词的用法
字符 | 含义 |
---|---|
'.+' |
匹配任意长度不为0且不含’\n’的字符串。’+’表示左边的’.’代表的任意字符出现1次或更多次。不要求出现的字符都必须一样。 |
'.*' |
匹配任意不含’\n’的字符串,包括空串 |
'[\dac]+' |
匹配长度不为0的由数字或’a’,’c’构成的串,如’451a’,’a21c78ca’ |
'\w{5}' |
匹配长度为5的由字母或数字或汉字构成的串,如’高大abc’,’33我a1 ‘ |
正则表达式示例
'[1-9]\d*'
正整数'-[1-9]\d*'
负整数'-?[1-9]\d*|0'
整数'[1-9]\d*|0'
非负整数'-?([1-9]\d*\.\d*[1-9]|0\.\d*[1-9]|0)'
左右都没有多余0的小数'\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*'
邮箱
邮箱示例:a.b.c.ddef+sfd@pku.edu.cn
正则表达式的函数
使用正则表达式
1 | import re |
re.match函数
re.match(pattern, string, flags=0)
- 从字符串string的起始位置匹配一个模式pattern
- flags标志位,用于控制模式串的匹配方式,如:是否区分大小写,多行匹配等等,如
re.M | re.|
表示忽略大小写, 且多行匹配 - 成功则返回一个匹配对象,否则返回None
1 | import re |
re.search函数
re.search(attern, string, flags = 0)
查找字符串中可以匹配成功的子串。
若匹配成功,则返回匹配对象;若无法匹配,则返回None。
1 | import re |
re.findall函数
re.findall(pattern, string, flags= 0)
查找字符串中所有和模式匹配的子串(不重叠)放入列表。一个子串都找不到就返回空表[]
1 | import re |
re.finditer函数
re.finditer (pattern, string, flags= 0)
查找字符串中所有和模式匹配的子串(不重叠),每个子串对应于一个匹配对象,返回匹配对象的序列(准确说是”可调用迭代器”)
如果找不到匹配子串,返回值也不为None或空表。假设返回值为r,则list(r)为[]
1 | import re |
1 | [32] (3, 7) |
re.sub替换匹配的子串
re.sub(模式串, 替换, 母串) 用于替换匹配的子串
1 | import re |
边界符号
\A
表示字符串的左边界,即要求从此往左边不能有任何字符\Z
表示字符串的右边界,即要求从此往右边不能有任何字符^
与\A
同。但多行匹配模式下还可以表示一行文字的左边界$
与\Z
同。但多行匹配模式下还可以表示一行文字的右边界
边界符号本身不会和任何字符匹配。
Python字符串 ‘\A’ , ‘\Z’ 都是两个字符,而不是像 ‘\N’ 那样的一个字符。
print(“\A\Z”) #>>\A\Z
\b
表示此处应为单词的左边界或右边界,即不可是单词字符\B
表示此处不允许是单词的左边界或右边界,即必须是单词字符
边界符号本身不会和任何字符匹配。
正则表达式的边界符号 ‘\b’ 是两个字符。但是在Python字符串中,’\b’和 ‘\t’, ‘\n’类似,是一个字符(Backspace)。
因此在正则表达式中使用边界符号\b,要写’\b’。如果写 ‘’\\b’,则连续的两个’\’被看作是一个普通的’\’,不会和后面的’b’一起被当作字符组合,变成边界符号’\b’。
print(“\b”) #>>\b
Python字符串’\B’是两个字符
1 | pt = "ka\\b.*" |
1 | import re |
分组(…)
括号中的表达式是一个分组。多个分组按左括号从左到右从1开始依次编号
1 | import re |
1 | import re |
- 在分组的右边可以通过分组的编号引用该分组所匹配的子串
1 | import re |
1 | pt = 'a(.)\\1*b' #或 pt = r'a(.)\1*b' |
- 分组作为一个整体,后面可以跟量词
1 | import re |
不要求分组的多次出现必须匹配相同字符串
re.findall和分组
- 在正则表达式中没有分组时,re.findall返回所有匹配子串构成的列表。有且只有一个分组时,re.fainall返回的是一个子串的列表,每个元素是一个匹配子串中分组对应的内容。
1 | import re |
- 在正则表达式中有超过一个分组时,re.findall返回的是一个元组的列表,每个元组对应于一个匹配的子串,元组里的元素,依次是1号分组、2号分组、3号分组……匹配的内容
1 | import re |
re.sub和分组
re.sub(模式串, 替换串, 母串)
用于替换匹配的子串
1 | import re |
| 的用法
- 表示”或”,如果没有放在”()”中,则起作用范围是直到整个正则表达式开头或结尾或另一个”|”
“\w{4}ce | c\d{3} | p\w”
可以匹配:
“ab12ce”
“c773”
“pk”
- 从左到右短路匹配(匹配上一个后就不计算是否还能匹配后面的)
1 | import re |
- ‘ | ‘ 也可以用于分组中,起作用范围仅限于分组内
1 | import re |
贪婪模式和懒惰模式
贪婪模式
- 量词
+,*,?,{m,n}
默认匹配尽可能长的子串
1 | import re |
懒惰模式
- 在量词
+,*,?,{m,n}
后面加’?’则匹配尽可能短的字符串。
1 | import re |
1 | import re |
匹配对象
匹配对象 (匹配成功时的返回结果)的属性
string
:匹配时使用的母串。
lastindex
:最后一个被匹配的分组的编号(不是最大编号)。如果没有被匹配的分组,将为None。
匹配对象的函数
group([n1,n2, ..])
- 获得一个或多个分组匹配的字符串;指定多个参数时将以元组形式返回。n1,n2… 可以使用编号也可以使用名字;
- 编号0代表整个匹配的子串(与模式里面的”()”无关);
- group()等价于group(0);
- 没有匹配字符串的组返回None;
- 匹配了多次的组返回最后一次匹配的子串。
groups([default])
以元组形式返回全部分组匹配的字符串。
相当于调用group(1,2…last)。
default表示没有匹配字符串的组以这个值替代,默认为None。
groupdict([default])
返回以有名字的组的名字为键、以该组匹配的子串为值的字典,没有名字的组不包含在内。
default表示没有匹配字符串的组以这个值替代,默认为None。
start([group])
返回指定的组匹配的子串在string中的起始位置。group默认值为0。
end([group])
返回指定的组匹配的子串在string中的结束位置(子串最后一个字符的位置+1)。group默认值为0。
span([group])
返回(start( group), end(group))。
group可以是组编号,也可以是组名字,缺省为0
1 | m = re.match(r'(\w+) (\w+) (.)', 'he1lo world!ss') |
应用实例
诸葛亮中的曹操
找出三国演义中,在诸葛亮提到曹操的所有场景,他是怎么说的
模式:
1 | 孔明曰:“倘曹操引兵来到,当如之何?” |
1 | import re |
抽取ip地址、邮箱、网址
1 | import re |
简化正则表达式的编写
一般来说,要写一个精确的正则表达式,比如写一个正则表达式来匹配ip地址,要做到匹配它的字符串一定是ip地址,且ip地址一定能匹配它,是比较困难的。
正则表达式可以写得宽容一些, 即ip地址一定能匹配它,但是能匹配它的不一定是ip地址。对匹配的字符串,另外再做一些额外的判断排除掉非ip地址,这样做比写精确的正则表达式容易。
例如:’\d+.(\d{1,3}.){2}\d+’
然后再split以后手动判断每一段是不是都小于255