Python模块 —— re正则表达式

一、正则表达式简介

  正则表达式是对字符串进行模糊匹配,提取自己需要的字符串部分,对于所有编程语言通用。对于Python来说我们使用re模块来操作正则表达式来匹配我们想要的结果。查看此篇文章,建议先看目录。
  体系结构简述如下:

正则
语法规则
字符
预定义字符集
数量词
边界匹配
逻辑、分组
特殊构造
Re模块
re.compile()
作为pattern
re.match()
从字符串开头匹配
re.search()
不从字符串开头匹配,但只匹配一次
re.split()
按照pattern分离字符串
re.findall()
找到所有符合条件的字符,返回列表
re.finditer()
找到所有符合条件的字符,返回一个迭代器
re.sub()
自环字符
re.subn()
返回一个re.sub()以及替换次数

二、正则表达式的语法规则

2.1 字符

语法说明表达式实例完整匹配的字符串
一般字符匹配自身abcabc
.匹配任意除换行符“\n”外的字符。
在DOTALL模式中也能匹配换行符。
a.cabc
\转义字符,使后面一个字符改变原来的意思。
如果字符串中有字符*匹配,可以使用\*或者字符集[*]
a\.c
a\\c
a.c
a\c
[...]字符集(字符类)。对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,如[abc][a-c]。第一个字符如果是^则表示取反,如[^abc]表示不是abc的其他字符。
所有的特殊字符在字符集中都失去其原有的特殊含义。在字符集中如果要使用]-^,可以在前面加上反斜杠,或者把]-放在第一个字符,把^放在非第一个字符。
a[bcd]eabe
ace
ade

2.2 预定义字符集(可以写在字符集[…]中)

语法说明表达式实例完整匹配的字符串
\d数字:[0-9] a\dca1c
\D非数字:[^\d]a\Dcabc
\s空白字符:[<空格>\t\r\n\f\v]a\sca c
\S非空白字符:[^\s]a\Scabc
\w单词字符:[A-Za-z0-9_]a\wcabc
\W非单词字符:[^\W]a\Wca c

2.3 数量词(用在字符或(…)之后)

语法说明表达式实例完整匹配的字符串
*匹配前一个字符0或无限次abc*ab
abccc
+匹配前一个字符1次或无限次abc+abc
abccc
?匹配前一个字符0次或1次abc?ab
abc
{m}匹配前一个字符m次ab{2}cabbc
{m,n}匹配前一个字符m至n次。
m和n可以省略:
  1、若省略m,则匹配0至n次。
  2、若省略n,则匹配m至无限次。
ab{1,2}cabc
abbc
*? +? ?? {m,n}?使得* + {m,n} 变成非贪婪模式。示例将在下文中介绍

2.4 边界匹配(不消耗待匹配字符串中的字符)

语法说明表达式实例完整匹配的字符串
^匹配字符串开头。
在多行模式中匹配每一行的开头。
^abcabc
$匹配字符串末尾。
在多行模式中匹配每一行的末尾。
abc$abc
\A仅匹配字符串开头。^效果一样,且仅Python独有。其他语言无此语法。\Aabcabc
\Z仅匹配字符串末尾。$效果一样,且仅Python独有。其他语言无此语法。abc\Zabc
\b仅匹配\w\W之间。匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。a\b!bca!bc
\B[^\b]匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。a\Bbcabc

2.5 逻辑、分组

语法说明表达式实例完整匹配的字符串
||代表左后表达式任意匹配一个。
他总是先尝试匹配左边的表达式,一旦成功匹配则跳过匹配右边的表达式。
如果|没有被包括在()中,则它的范围是整个正则表达式。
abc|defabc
def
(...)被括起来的表达式将作为分组,从表达式左边开始没遇到一个分组的左括号(,编号+1。
另外,分组表达式作为一个整体,以后可以接数量词。表达式中的`
`仅在该组中有效。(abc){2}
a(123|456)c
(?P<name>...)分组,除了原有的编号外再指定一个额外的别名。(?P<id>abc){2}abcabc
\<number>引用编号为<number>的分组匹配到的字符串。(\d)abc\11abc1
5abc5
注:相当于(\d)匹配的字符再用了一次。
(?P=name)引用别名为<name>的分组匹配到的字符。(?P<id>\d)abc(?P=id)1abc1
5abc5

2.6 特殊构造(不作为分组)

语法说明表达式实例完整匹配的字符串
(?:...)(...)的不分组版本,用于使用|或后接数量词。(?:abc){2}abcabc
(?iLmsux)iLmsux的每一个字符代表一个匹配模式,只能用在正则表达式的开头,可选多个。匹配模式将在下文中介绍。(?i)abcAbC
(?#...)#后的内容将作为注释被忽略。在这里插入图片描述abc(?#comment)123abc123
(?=...)之后的字符串内容需要匹配表达式才能成功匹配。
不消耗字符串内容[1]
a(?=\d)后面是数字的a
(?!...)之后的字符串内容需要不匹配表达式才能成功匹配。
不消耗字符串内容[1]
a(?!\d)后面不是数字的a
(?<=...)之前的字符串内容需要匹配表达式才能成功匹配。
不消耗字符串内容[1]
(?<=\d)a前面是数字a
(?<!...)之前的字符串内容需要不匹配表达式才能成功匹配。
不消耗字符串内容[1]
(?<!\d)a前面不是数字的a
(?(id/name)yes-pattern|no-pattern如果编号为id/别名为name的组匹配到字符,则需要匹配yes-patttern,否则需要匹配no-pattern。|no-pattern可以省略。(\d)abc(?(1)\d|abc)1abc2
abcabc

2.7 正则表达式相关说明

(1) 数量词的贪婪模式与非贪婪模式
  正则表达式通常用于在文本中查找匹配的字符串。Python里的数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则
相反,总是尝试匹配尽可能少的字符。例如:正则表达式"ab*“如果用于查找"abbbc”,将找到"abbb"。而如果使用非贪婪的数量词"ab*?“,将找到"a”。

*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

注:*?如果在表达式的末尾,那么就无法匹配到字符,也就是匹配前一个字符0次,但是如果是在表达式中,为了匹配*?之后的字符,我们就有可能匹配到多个。

.*?的用法

.是任意字符
*是匹配前一个字符0次或无限次
?是非贪婪模式。
合在一起就是取尽量少的任意字符,一般不会这么单独写,他大多数用在:
.*?x就是取前面任意长度的字符,直到一个x出现

(2)不消耗字符串内容
  正则表达式是对给定的字符串进行匹配。也就是说,一般匹配了一个字符后,该字符就被消耗,就不能被Regular Expression的其他部分匹配了。
  但是预查不是,因为他不消耗字符。示例如下代码:

import re
re.search(r'abc', 'abcdefg')
<re.Match object; span=(0, 3), match='abc'>

re.search(r'a(b)bc', 'abcdefg') # 未匹配出结果,因为字符串中b被(b)消耗掉了,没有另外的b符合括号外的b

re.search(r'a(b)bc', 'abbcdefg') # 匹配出的结果,字符串中有两个b可供消耗
<re.Match object; span=(0, 4), match='abbc'>

re.search(r'a(?=b)bc', 'abcdefg') # 匹配成功,因为第一个(?=b)中的b不消耗字符串内容,故还留有一个b被括号外的b消耗
<re.Match object; span=(0, 3), match='abc'>

(3)转义字符
  与大多数编程语言相同,正则表达式里使用”\”作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符”\”,那么使用编程语言表示的正则表达式里将需要4个反斜杠”\\”:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
  Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r”\”表示。同样,匹配一个数字的”\d”可以写成r”\d”。

三、Python Re模块

3.1 简介

  Python 自带了re模块,它提供了对正则表达式的支持。
  上文中我们介绍了很多正则表达式的语法,我们需要上面的语法写出一个表达式,符合这个表达式规则的文本,就是我们想要的内容。而这个表达式我们也称为pattern

3.2 标志 flags

简写全称用法
re.Are.ASCII让 \w, \W, \b, \B, \d, \D, \s 和 \S 只匹配ASCII,而不是Unicode。不是很理解,暂时还没有看到能用到的地方。
re.Mre.MULTILINE多行模式,改变^$的行为。使其可以匹配多行(也就是\n为一行结束)的开头和结尾。
在 ‘foo1\nfoo2\n’ 中搜索 foo.$,通常匹配 ‘foo2’,但在 re.MULTILINE模式下可以匹配到 ‘foo1’;在 ‘foo\n’ 中搜索 $会找到两个(空的)匹配:一个在换行符之前,一个在字符串的末尾。
re.Sre.DOTALL点任意匹配模式,改变.的行为。
.特殊字符匹配任何字符,包括换行符;如果没有这个标记,.就匹配 除了 换行符的其他任意字符。
re.Ire.IGNORECASE忽略大小写
·re.Lre.LOCALE使预定字符类\w \W \b \B \s \S 取决于当前区域设定
re.Ure.UNICODE使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
re.Xre.VERBOSE详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。

3.3 re.compile(pattern, flags=0)

   Pattern就是我们需要写的那个正则表达式,我们需要通过这个表达式去匹配到我们需要的字符串从而进行判断、提取以及替换等操作。
  在Python中compile(pattern, flags=0)方法可以让我们获得一个pattern对象。其中pattern是我们需要填写的表达式,flags就是我们上一节提到的标志。书写和使用方法主要是一下三种:

  1. 如果需要多次使用这个正则表达式的话,使用 re.compile() 和保存这个正则对象以便复用,可以让程序更加高效。
prog = re.compile(pattern)
result = prog.match(string)
  1. 也可以用如下书写方式,不过不是很推荐。
prog = re.compile(pattern)
result = re.match(prog)
  1. 如果只使用一次,那我们直接写成如下方式比较方便。
result = re.match(pattern, string)

3.4 re.match(pattern, string[, flags])

  如果 string 开头的零个或多个字符与正则表达式 pattern 匹配,则返回相应的 Match。 如果字符串与模式不匹配则返回 None;请注意这与零长度匹配是不同的。
  注意即便是 MULTILINE 多行模式, re.match() 也只匹配字符串的开始位置,而不匹配每行开始。
如果你想定位 string 的任何位置,使用 search() 来替代。

import re

pattern = re.compile(r'abc')
result1 = pattern.match('abcdefabc')  # result1: <re.Match object; span=(0, 3), match='abc'>
result2 = pattern.match('defabc')  # result2: None

  有两点需要注意:

  1. re.match() 只从字符串开头匹配,从result1和result2的匹配结果可以看出,result2中字符串末尾的’abc’并没有被匹配到。
  2. 匹配对象总是有一个布尔值 True。如果没有匹配的话 match() 和 search() 返回 None 所以你可以简单的用 if 语句来判断是否匹配。
# 接上一个代码块中的结果
if result1:
    print(True)
if not result2:
    print(False)

3.4.1 match的属性

  • string: 匹配时使用的文本
  • re: 匹配时使用的Pattern对象
  • pos: 文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同
  • endpos: 文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
  • lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。
  • lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。
3.4.1.1 string

  匹配时使用的文本,也就是re.match(pattern, string, flags=0)中的string

import re

pattern = re.compile(r'(abc\d+)(de)f')
result1 = pattern.match('abc123defabc')
result2 = pattern.match('abc12fabc')
string_value1 = result1.string  # string_value1: 'abc123defabc'
string_value2 = result2.string  # 会报错,因为result2没有匹配到值,结果为None,所以没有string属性
3.4.1.2 re

  匹配时使用的Pattern对象。也就是re.match(pattern, string, flags=0)中的pattern

import re

pattern = re.compile(r'(abc\d+)(de)f')
result1 = pattern.match('abc123defabc')
result2 = pattern.match('abc12fabc')
re_value1 = result1.re  # re_value1: re.compile('(abc\\d+)(de)f')
3.4.1.2 pos

  文本中正则表达式开始搜索的索引。值与Pattern.match()Pattern.seach()方法的同名参数相同。好像只要match()正则匹配成功,这个属性值就必然为0,暂时没遇到例外的情况

import re

pattern = re.compile(r'(abc\d+)(de)f')
result1 = pattern.match('abc123defabc')
result2 = pattern.match('abc12fabc')
pos_value = result1.pos  # pos_value: 0
3.4.1.3 endpos

  文本中正则表达式结束搜索的索引。值与Pattern.match()Pattern.seach()方法的同名参数相同。

import re

pattern = re.compile(r'(abc\d+)(de)f')
result1 = pattern.match('abc123defabc')  # 字符长度为12
result2 = pattern.match('abc12fabc')
endpos_value = result1.endpos  # endpos_value: 12
3.4.1.4 lastindex

  最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。这里提到了一个分组的概念,这就是我们前面介绍的(...)会捕获分组并编号或命名,编号为1, 2。在下面代码中有两个(...)所以捕获两个分组。lastindex标识最后一个被捕获分组的索引,也就是(de)索引(也就是上面的编号)为2。

import re

pattern = re.compile(r'(abc\d+)(de)f')
result1 = pattern.match('abc123defabc')
result2 = pattern.match('abc12fabc')
lastindex = result1.lastindex  # lastindex: 2
3.4.1.5 lastgroup

  最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。

import re

pattern1 = re.compile(r'(abc\d+)(de)f')
result1 = pattern1.match('abc123defabc')
lastgroup1 = result1.lastgroup  # lastgroup1: None  # 因为没有分组命名,故为None

pattern2 = re.compile(r'(abc\d+)(?P<name>de)f')
result2 = pattern2.match('abc123defabc')
lastgroup2 = result2.lastgroup  # lastgroup2: 'name' # 正则表达式中给分组命名为了name

3.4.2 match的方法

  • group([group1, …]):获得一个或多个分组截获的字符串
  • groups([default]):以元组形式返回全部分组截获的字符串。
  • groupdict([default]):返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。
  • start([group]):返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。
  • end([group]):返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。
  • span([group]):返回(start(group), end(group))。
  • expand(template):将匹配到的分组代入template中然后返回。
3.4.2.1 group([group1, …])

  获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。

import re

pattern1 = re.compile(r'(abc\d+)(de)f')
result1 = pattern1.match('abc123defabc')
group0 = result1.group()  # group0: 'abc123def'
group1 = result1.group(1)  # group1: 'abc123'
group2 = result1.group(2)  # group2: 'de'
3.4.2.2 groups([default])

  以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。

import re

pattern1 = re.compile(r'(abc\d+)(de)f')
result1 = pattern1.match('abc123defabc')
groups = result1.groups()  # groups: ('abc123', 'de')
3.4.2.3 groupdict([default])

  返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。

import re

pattern1 = re.compile(r'(abc\d+)(de)f')
result1 = pattern1.match('abc123defabc')
groupdic1 = result1.groupdict()  # groupdic1: {} # 这里由于没对分组命名,所以取不到键值对

pattern2 = re.compile(r'(?P<name>abc\d+)(de)f')
result2 = pattern2.match('abc123defabc')
groupdic2 = result2.groupdict()  # groupdic2: {'name': 'abc123'} # 这里取到有分组命令的键值对
3.4.2.4 start([group])

  返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。

import re

pattern1 = re.compile(r'(abc\d+)(de)f')
result1 = pattern1.match('abc123defabc')
start0 = result1.start()  # start0: 0
start2 = result1.start(2)  # start2: 6 #第二个分组的开始位置,也就是字母d的开始位置
3.4.2.5 end([group])

  返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。

import re

pattern1 = re.compile(r'(abc\d+)(de)f')
result1 = pattern1.match('abc123defabc')
end0 = result1.end()  # end0: 9
end2 = result1.end(2)  # end2: 8
3.4.2.6 span([group])

  返回(start(group), end(group))。

import re

pattern1 = re.compile(r'(abc\d+)(de)f')
result1 = pattern1.match('abc123defabc')
span0 = result1.span()  # span0: (0, 9) 
span2 = result1.span(2)  # span2: (6, 8)
3.4.2.7 expand(template)

  将匹配到的分组代入template中然后返回。template中可以使用\id或\g、\g引用分组,但不能使用编号0。\id与\g是等价的;但\10将被认为是第10个分组,如果你想表达\1之后是字符’0’,只能使用\g0。暂时没找到相应的使用示例。

3.5 re.search(pattern, string[, flags])

  search方法与match方法极其类似,区别在于match()函数只检测re是不是在string的开始位置匹配,search()会扫描整个string查找匹配,match()只有在0位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回None。同样,search方法的返回对象同样match()返回对象的方法和属性

import re

pattern1 = re.compile(r'abc')
result1 = pattern1.search('qqqabc123defabc')  # result1: <re.Match object; span=(3, 6), match='abc'> #只匹配从左到右第一个能匹配到的字符,第二个abc无法匹配和获取
result2 = pattern1.search('qqq')

  有两点需要注意:

  1. re.search()不需要像re.match()只从字符串开头匹配。但是从上述代码中看到,只能匹配一次。
  2. 匹配对象总是有一个布尔值 True。如果没有匹配的话 match() 和 search() 返回 None 所以你可以简单的用 if 语句来判断是否匹配。
# 接上一个代码块中的结果
if result1:
    print(True)
if not result2:
    print(False)

3.6 re.split(pattern, string[, maxsplit])

  按照能够匹配的子串将string分割后返回列表。maxsplit用于指定最大分割次数,不指定将全部分割。

import re

pattern1 = re.compile(r'\d+')
result1 = pattern1.split('one1two2three3four4')  # result1: ['one', 'two', 'three', 'four', ''] # 匹配到的字符不在分割后的内容里

3.7 re.findall(pattern, string[, flags])

  搜索string,以列表形式返回全部能匹配的子串。

import re

pattern1 = re.compile(r'\d+')
result1 = pattern1.findall('one1two2three3four4')  # result1: ['1', '2', '3', '4'] # 这返回的是一个列表可以使用列表的操作

  返回结果取决于模式中捕获组的数量。如果没有组,返回与整个模式匹配的字符串列表。如果有且仅有一个组,返回与该组匹配的字符串列表。如果有多个组,返回与这些组匹配的字符串元组列表。非捕获组不影响结果。

import re

pattern1 = re.compile(r'e\d+')
result1 = pattern1.findall('one1two2three3four4') # result1: ['e1', 'e3']  # 没有组的时候返回匹配到的字符串的列表

pattern2 = re.compile(r'e(\d+)')
result2 = pattern2.findall('one1two2three3four4')  # result1: ['1', '3']  # 有组的时候返回的是匹配到字符串里面分组的列表

pattern3 = re.compile(r'e(\d+).*?(\d+)')
result3 = pattern3.findall('one1two2three3four4')  # result3: [('1', '2'), ('3', '4')]  # 非贪婪模式

pattern4 = re.compile(r'e(\d+).*(\d+)')
result4 = pattern4.findall('one1two2three3four4')  # result4: [('1', '4')]  # 贪婪模式

3.8 re.finditer(pattern, string[, flags])

  搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。

import re

pattern1 = re.compile(r'\d+')
result1 = pattern1.finditer('one1two2three3four4') # result1: <callable_iterator object at 0x000002C689E0ADF0>
for i in result1:
    print(i)  # 输出1 2 3 4

3.9 re.sub(pattern, repl, string[, count])

  使用repl替换string中每一个匹配的子串后返回替换后的字符串。
  当repl是一个字符串时,可以使用\id或\g、\g引用分组,但不能使用编号0。
  当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
  count用于指定最多替换次数,不指定时全部替换。
  更高级用法参见 re.sub

import re
pattern1 = re.compile(r'([a-zA-Z]+)(\d+)')
findall1 = pattern1.findall('one1two2three3four4')  # findall1: [('one', '1'), ('two', '2'), ('three', '3'), ('four', '4')]
sub1 = pattern1.sub(r'\2\1', 'one1two2three3four4')  # sub1: '1one2two3three4four'

3.10 re.subn(pattern, repl, string[, count])

  返回 (sub(repl, string[, count]), 替换次数)。也就是返货sub的替换结果和替换次数,形式为元组

import re

pattern1 = re.compile(r'([a-zA-Z]+)(\d+)')
findall1 = pattern1.findall('one1two2three3four4')  # findall1: [('one', '1'), ('two', '2'), ('three', '3'), ('four', '4')]
subn1 = pattern1.subn(r'\2\1', 'one1two2three3four4')  #subn1: ('1one2two3three4four', 4)

参考文章:

  1. re模块详解
  2. re — 正则表达式操作(官方文档)
  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值