Python之路(第二十一篇) re模块
正则表达式本身是一种小型的、高度专业化的编程语言,正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re,正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
匹配语法
re方法(匹配模式,字符串,flag)
(一)字符
1、元字符
普通字符 | 匹配内容 |
---|---|
. | 匹配任意除换行符"\n"外的字符,若指定flag DOTALL,则匹配任意字符,包括换行 |
\ | 转义字符,使后一个字符改变原来的意思 |
* | 匹配前一个字符0或多次 |
+ | 匹配前一个字符1次或无限次 |
? | 匹配一个字符0次或1次, 和防止贪婪匹配 |
^ | 匹配字符串开头。在多行模式中匹配每一行的开头,^元字符如果写到[]字符集里就是反取 |
$ | 匹配字符串末尾,在多行模式中匹配每一行的末尾 |
| | 或。匹配|左右表达式任意一个,从左到右匹配,如果|没有包括在()中,则它的范围是整个正则表达式 |
{} | {m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次 |
[] | 字符集。对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,如[abc]或[a-c]。abc表示取反,即非abc。所有特殊字符在字符集中都失去其原有的特殊含义。用\反斜杠转义恢复特殊字符的特殊含义。 |
() | 被括起来的表达式将作为分组,从表达式左边开始没遇到一个分组的左括号“(”,编号+1.分组表达式作为一个整体,可以后接数量词。表达式中的|仅在该组中有效。 匹配任何在圆括号内的正则表达式, 并表明分组的开始和结束; 分组的内容在完成匹配后可以提取出来,而且可以在后面的字符串中用特殊的number 序列匹配 |
量词
量词 | 用法说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
元字符匹配次数说明
例子
import re
# '.'字符 , 从开头一直找 ,返回找到的所有字符
print(re.findall("nichol..","nicknicholas")) #['nicholas']
#'^'匹配字符串开头。在多行模式中匹配每一行的开头
print(re.findall("^nick","nickcalnicholas")) #['nick']
#'$'匹配字符串末尾,在多行模式中匹配每一行的末尾
print(re.findall("ick$","nicholasnicknick")) #['ick']
# '*' 匹配*号前的字符0次或多次
print(re.findall("a*","abcabaad")) #['a', '', '', 'a', '', 'aa', '', '']
# '+' 匹配前一个字符1次或多次
print(re.findall("a+","abcabaad")) #['a', 'a', 'aa']
# '?' 匹配前一个字符1次或0次
print(re.findall("a?","abcabaad")) #['a', '', '', 'a', '', 'a', 'a', '', '']
# '|' 或。匹配|左右表达式任意一个,从左到右匹配,如果|没有包括在()中,则它的范围是整个正则表达式
print(re.findall("(ab|AC)","abcadxyzab")) #['ab', 'ab']
print(re.findall("(ab|AC)","ACxyznick")) #['AC']
# '{m}'匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次
print(re.findall("a{2,4}","abcaadaxyzaaaa")) #['aa', 'aaaa']
# '[]'字符集。对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,如[abc]或[a-c]。
print(re.findall("[ac]","acxyzabc")) #['a', 'c', 'a', 'c']
print(re.findall("[^ac]","acxyzabc")) #['x', 'y', 'z', 'b']
print(re.findall('a[bc]d','acd')) #['acd']
print(re.findall('[a-z]','acd')) #['a', 'c', 'd']
print(re.findall('[1-9]','45dha3')) #['4', '5', '3']
print(re.findall('[^ab]','45bdha3')) #['4', '5', 'd', 'h', '3']
print(re.findall('[\d]','45bdha3')) #['4', '5', '3']
# '()'匹配被括起来的内容
print(re.findall("(ab)",'xyz453d6gab89uzaxby')) #['ab']
print(re.findall("(ab)",'xyz453d6gab89uzaby')) #['ab', 'ab']
print(re.findall(r'(ad)+', 'addhahdwha2e3adadad')) #['ad', 'ad']
2、预定义字符集
预定义字符 | 匹配说明 |
---|---|
\d | 数字:[0-9] 字符英文说明digit |
\D | 等同于非数字:[^0-9] |
\s | 匹配任意的空白符:[<空格>\t\r\n\f\v] 可以匹配空格、换行符、缩进符号等 . 字符英文说明space |
\S | 非空白字符:[^\s] |
\w | 匹配包括下划线在内的任何字字符:[A-Za-z0-9_](大小写的A-Z,数字0-9) 字符英文说明word |
\W | 匹配非字母字符,即匹配特殊字符 |
\A | 仅匹配字符串开头,同^ |
\Z | 仅匹配字符串结尾,同$ |
\b | 匹配\w和\W之间,即匹配单词边界匹配一个单词边界,也就是指单词和空格间的位置。例如, 'lo\b' 可以匹配"hello" 中的 'lo',但不能匹配 "hellohi" 中的 'lo'。 |
\B | [^\b]匹配所有的非边界字符 |
例子
import re
# 匹配数字:[0-9]
print(re.findall("\d","你好我11大22,求十大3")) #['1', '1', '2', '2', '3']
#匹配非数字[0-9]
print(re.findall("\D","你好我11大22,求十大3")) #['你', '好', '我', '大', ',', '求', '十', '大']
# 匹配任意的空白符(打印不出来的)
print(re.findall("\s","你好我好\n,大家好\t")) #['\n', '\t']
# 匹配非空白字符:[^\s]
print(re.findall("\S","你好我好\n,大家好\t")) #['你', '好', '我', '好', ',', '大', '家', '好']
# 匹配包括下划线在内的任何字字符:[A-Za-z0-9_](大小写的A-Z,数字0-9),python中支持中文
print(re.findall("\w","ni好2_,大家好,HI\n")) #['n', 'i', '好', '2', '_', '大', '家', '好', 'H', 'I']
# 仅匹配字符串开头,同^
print(re.findall("\Ah","hell hello")) #['h']
# 仅匹配字符串结尾,同$
print(re.findall("o\Z","hellhello")) #['o']
# \b匹配\w和\W之间,即匹配单词边界匹配一个单词边界,也就是指单词和空格间的位置。
print(re.findall("\\b","hello word")) #['', '', '', ''] 把两个单词左右的空格边界匹配到了这里多加一个斜杠是为了转义
print(re.findall(r"\b","hello word")) #['', '', '', ''],或者不转义直接加r
3、字符集[]
字符集 | 说明 |
---|---|
[0-9] | 也可以用-表示范围,[0-9]就和[0123456789]是一个意思` |
[a-z] | 同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示 |
[A-Z] | [A-Z]就表示所有的大写字母 |
[0-9a-fA-F] | 可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
[A-z] | 可以匹配所有的大写字母和小写字母 |
例子
import re
print(re.findall("[0-9]+","add1ab21cc332ab")) #['1', '21', '332']
print(re.findall("[2-9]+","add1ab21cc332ab")) #['2', '332']
print(re.findall("[a-z]+","aadddccda")) #['aadddccda']
print(re.findall("[c-z]+","aadddcacda")) #['dddc', 'cd']
print(re.findall("[A-z]","A,BC,abc")) #['A', 'B', 'C', 'a', 'b', 'c']
4、()元字符,分组
也就是分组匹配,()里面的为一个组也可以理解成一个整体
如果()后面跟的是特殊元字符如 (adc)* , 那么*控制的前导字符就是()里的整体内容,不再是前导一个字符
例子
import reprint(re.findall("(ab)+","addabccab")) #['ab', 'ab']
5、r原生字符
将在python里有特殊意义的字符如\b,转换成原生字符(就是去除它在python的特殊意义),不然会给正则表达式有冲突,为了避免这种冲突可以在规则前加原始字符r,或者加上转义符\
例子
print(re.findall("\\b","hello word"))
print(re.findall(r"\b","hello word"))
r是real的意思
6、注意
(1) ^有两种用法,一种是作为开头,表示匹配字符串开头。第二种是非,在字符集里用表示非
如[^0-9]
表示匹配非数字0-9
(2)在正则表达式中,有很多有特殊意义的是元字符,比如\d和\s等,如果要在正则中匹配正常的"\d"而不是"数字"就需要对"\"进行转义,变成'\'。
在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中\也有特殊的含义,本身还需要转义。所以如果匹配一次"\d",字符串中要写成'\d',那么正则里就要写成"\\d",这样就太麻烦了。这个时候我们就用到了r'\d'这个概念,此时的正则是r'\d'就可以了。
(3)注意^和$使用所在的位置。匹配开头^要在被匹配的字符前面即“^a”,匹配结尾 $要在被匹配的字符后面即"a$"
正则 | 待匹配字符 | 匹配结果 | 说明 |
---|---|---|---|
\d | \d | False | 因为在正则表达式中\是有特殊意义的字符,所以要匹配\d本身,用表达式\d无法匹配 |
\d | \d | True | 转义\之后变成\\,即可匹配 |
"\\d" | '\d' | True | 如果在python中,字符串中的'\'也需要转义,所以每一个字符串'\'又需要转义一次 |
r'\d' | r'\d' | True | 在字符串之前加r,让整个字符串不转义 |
7、贪婪匹配与非贪婪匹配
*?, +?, ??, {m,n}?
前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配
满足匹配时,匹配尽可能长的字符串,默认情况下,采用贪婪匹配,加上?变成非贪婪匹配(惰性匹配)
正则 | 待匹配字符 | 匹配结果 | 说明 |
---|---|---|---|
<.*> | <script>...<script> | <script>...<script> | 默认为贪婪匹配模式,会匹配尽量长的字符串 |
<.*?> | <script>...<script> | <script><script> | 加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串 |
例子
import re
print(re.findall("<.*>","<script>...<script>")) #['<script>...<script>']
print(re.findall("<.*?>","<script>...<script>")) #['<script>', '<script>']
分析:<.*>是贪婪匹配,从左直接匹配到最右侧,然后向左回退到最后一个“>”,这就是回溯算法
"<.*?>"是非贪婪匹配,匹配时是从左向右匹配,遇到一个“>”马上返回,继续匹配,遇到一个遇到一个“>”马上返回。
几个常用的非贪婪匹配Pattern
*? 重复任意次,但尽可能少重复+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x
就是取前面任意长度的字符,直到一个x出现
(二)re模块中常用功能函数
1、compile()
编译正则表达式模式,返回一个对象的模式。(可以把那些常用的正则表达式编译成正则表达式对象,这样可以提高一点效率。)
格式
re.compile(pattern,flags=0)
pattern: 编译时用的表达式字符串。
flags 编译标志位,用于修改正则表达式的匹配方式,如:是否区分大小写,多行匹配等。常用的flags有:
标志 | 含义 |
---|---|
re.S(DOTALL) | 使.匹配包括换行在内的所有字符 |
re.I(IGNORECASE) | 使匹配对大小写不敏感 |
re.L(LOCALE) | 做本地化识别(locale-aware)匹配,法语等 |
re.M(MULTILINE) | 多行匹配,影响^和$ |
re.X(VERBOSE) | 该标志通过给予更灵活的格式以便将正则表达式写得更易于理解 |
re.U | 根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B |
补充:
- re.S:
- 使 . 匹配包括换行在内的所有字符
- re.M:
- 进行多行匹配,在新的一行中,同样可以使用^来匹配该行字符串的开头,用$来匹配该行字符串的结尾
例子
#匹配出i开头的行string = '''fall in love with you
i love you very much
i love she
i love her'''
print(re.findall('^i.*',string,re.S))
print(re.findall('^i.*',string,re.M))
输出结果
[]['i love you very much', 'i love she', 'i love her']
例子
import re
p = re.compile("\d+") #创建一个常用的规则
res = p.search("abc123sxajk")
print(res) #search直接返回的是一个match对象,需要调用group()方法获取匹配字符串
print(res.group())
输出结果
<_sre.SRE_Match object; span=(3, 6), match='123'>123
2、search()
函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
格式
re.search(pattern, string, flags=0)
例子
import re
res = re.search("\d+","abc123sxajk")
print(res) #search直接返回的是一个match对象,需要调用group()方法获取匹配字符串
print(res.group())
输出结果
<_sre.SRE_Match object; span=(3, 6), match='123'>123
3、match()
同search,不过仅在字符串开始处进行匹配
格式:
re.match(pattern, string, flags=0)
例子
import re
res = re.match("\d+","123sxajk")
print(res) #match匹配要从开头处匹配,match也是直接返回的是一个match对象,需要调用group()方法获取匹配字符串
print(res.group()) #如果匹配不到返回的是None,这时调用group()方法会报错
输出结果
<_sre.SRE_Match object; span=(0, 3), match='123'>
123
分析:match()方法是从字符串的开头处匹配的,如果匹配不到则返回None,这时调用group()方法会报错,因为None没有group()方法。
*注:match和search一旦匹配成功,就是一个match object对象,而match object对象有以下方法:
group() 返回被 RE 匹配的字符串
start() 返回匹配开始的位置
end() 返回匹配结束的位置
span() 返回一个元组包含匹配 (开始,结束) 的位置
group() 返回re整体匹配的字符串,可以一次输入多个组号,对应组号匹配的字符串。
a. group()返回re整体匹配的字符串,a. group (n,m) 返回组号为n,m所匹配的字符串,如果组号不存在,则返回indexError异常a.groups()groups() 方法返回一个包含正则表达式中所有小组字符串的元组,从 1 到所含的小组号,通常groups()不需要参数,返回一个元组,元组中的元素就是正则表达式中定义的组。
4、findall()
返回所有满足匹配条件的结果,放在列表里
格式:
re.findall(pattern, string, flags=0)
import re
print(re.findall("a","abc sxa dda"))
输出结果
['a', 'a', 'a']
5、finditer()
搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。
格式:
re.finditer(pattern, string, flags=0)
import re
res = re.finditer("\d","dak2fhr34ehnb567grd")
print(res) #返回的是一个可调用的迭代器
print(type(res))
print(next(res)) #通过迭代器的next()方法取出每个值,这里的每个值是一个match对象
print(next(res).group()) #通过调用match对象的group()方法来取出匹配出的值
输出结果
<callable_iterator object at 0x02655870><class 'callable_iterator'>
<_sre.SRE_Match object; span=(3, 4), match='2'>
3
6、split()
按照能够匹配的子串将string分割后返回列表。
格式:
re.split(pattern, string[, maxsplit])
import re
print(re.split("[ab]","abcdeg"))
输出结果
['', '', 'cdeg']
分析:
执行过程:首先以"a"分割字符串“abcdeg",得到结果["","bcdeg"],即一个空白字符和”bcdeg“,然后再用”b“对这个结果再次进行分割,得到['', '', 'cdeg'],即两个空白字符和‘cdeg’.
7、sub()
使用re替换string中每一个匹配的子串后返回替换后的字符串。
格式:
re.sub(pattern, repl, string, count)
例子
import re
print(re.sub("a","s","hahahah"))
输出结果
hshshsh
8、subn()
替换string中匹配子串,但返回的是元组(替换的结果,替换了多少次)
格式:
subn(pattern, repl, string, count=0, flags=0)
例子
import re
print(re.subn("a","s","hahahah"))
输出结果
('hshshsh', 3)
三、重要补充
1、findall的分组优先
如果re.findall()规则里有分组(括号),那么findall只能返回分组里的内容。
例子
res = re.findall("www.(baidu|google).com","www.google.com")print(res)
输出结果
['google']
分析:注意这里只能匹配返回出分组里的内容,而非整体的字符串。
想要整体返回需要在分组里加?:取消分组优先模式(问号在正则表达式里的第三个功能,第一个表示匹配次数,第二个是实现惰性匹配)
import re
res = re.findall("www.(?:baidu|google).com","www.google.com")
print(res)
输出结果
['www.google.com']
2、 split的优先级
re.split()方法在加上分组之后可以返回被匹配分割去掉的项。在匹配部分加上()之后所切出的结果是不同的,没有()的没有保留所匹配的项,但是有()的却能够保留了匹配的项
例子
import re
res1 = re.split("\d+","hello5world5hi2")
res2 = re.split("(\d+)","hello5world5hi2")
print(res1)
print(res2)
输出结果
['hello', 'world', 'hi', '']['hello', '5', 'world', '5', 'hi', '2', '']
以上是 Python之路(第二十一篇) re模块 的全部内容, 来源链接: utcz.com/z/388604.html