处理引号也可以用转义字符:Python 并不明确区分单双引号,只要要匹配使用就行;如要表示一个字面意义上的引号,需用另外一种引号来嵌套,或者转义字符:
"'" # use another kind of qoute
"\"" # use escape character
输入这些符号时需要多次敲键盘,但 Python 都把它们当成长度为1
的字符来处理:
In [1]: len("\017")
Out[1]: 1
In [2]: len("\n")
Out[2]: 1
In [3]: len("\\")
Out[3]: 1
1.2 Python 中转义序列标志符
反斜杠是 Python 中转义序列标志符,它有如下特性需要注意:
表示一个字面意义的\
需要转义序列标志符\
Python 中显示一个字面意义上\
则是\\
In [1]: len("")
Out[1]: 0
In [2]: len("\")
File "<ipython-input-2-553b6950c0a1>", line 1
len("\")
SyntaxError: EOL while scanning string literal
In [3]: len("\\")
Out[3]: 1
In [4]: "\\"[0]
Out[4]: "\\"
转义序列标志只有在特定的字符组合前才有效果,如果后面的字符组合不能组合为转义,则只会被当成简单的字符。
比如\n
是特殊含义,而\m
没有,\m
只会被看成是一个字面上的\
和m
组成的长度为 2 的字符串。
1.3 原始字符串
在某些特定情况下希望字符是所见即所得。一个\
和n
组合在一起就是表示两个字符,而不是换行符。此时可以用 Python 的原始字符串(Raw String),在要处理的字符前面加上r
:
In [1]: len(r"\n")
Out[1]: 2
In [2]: len(r"\")
File "<ipython-input-6-aa225c032aa6>", line 1
len(r"\")
SyntaxError: EOL while scanning string literal
In [3]: len(r"\\")
Out[3]: 2
原始字符串并非万能:单独的反斜杠\
没法用原始字符串表示,2 中把最后的引号给转义掉,3 是一个原始字符串,但表示两个反斜杠。无法用原始字符串表是转义序列。
2 Python 正则引擎
用于匹配的程序叫正则表达式引擎,Python 自带re
的模块。当然还有些第三方库正则引擎,但并不是主要介绍的对象。需要提前导入re
模块:
In [1]: import re
同时,关于匹配的详细细节也不是本文介绍的重点。本文主要介绍正则引擎常用函数的使用,常见函数有两种用法:
re.func(REG, string, flag) # call the func with re directly
re.compile(REG, flag).func(string) # call the compiled object
通常认为先 compile(compile
是re
模块的函数),后运算可以提升效率。但实际上并不特别关注效率问题:
某些高效的正则表达式可能难以理解
Python 本身运行就想对慢一些
本文主要介绍使用,并不过分关注原理
函数参数意义如下:
REG
(regular expression)就是正则表达式,第三部分会详细介绍。
string
是要匹配的字符串,Python 字符串。
flag
是正则运算时候的参数,会在介绍具体函数时候介绍。
常见的func
是:
match
匹配字符串的开始,返回一个re.Match
(匹配上)或None
(没匹配上)
search
找到一个匹配就返回,即使有多个也只返回第一个,返回一个re.Match
(匹配上)或者None
(没匹配上)
findall
找到所有匹配,返回一个列表(第四部分详细介绍这个列表)
finditer
找到所有匹配,返回一个迭代器
split
对字符串按照一定规则切分,返回一个列表
sub
对字符串进行替换,返回替换后的字符串
subn
对字符串进行替换,返回 tuple,前面是替换后的字符,后面是总共替换次数
所谓re.Match
,有如下方法:
re.match.span() # span, tuple
re.match.start() # start index
re.match.end() # end index
re.match.group() # match group
re.match.group_dict() # match group
其中group
表示匹配的组, 会在后面捕获组一节中详细介绍。这里可以通过一个例子就re.Match
进行简单说明。由于使用例子要用到正则表达式,这里先给出正则表达式的一条规则:大部分字符都匹配自身。
In [1]:x = re.search("abc", 'zyxabcdre')
In [2]: x
Out[2]: <re.Match object; span=(3, 6), match='abc'>
In [3]: x.start()
Out[3]: 3
In [4]: x.end()
Out[4]: 6
In [5]: x.span()
Out[5]: (3, 6)
In [6]: x.group()
Out[6]: 'abc'
In [7]: x.group(0)
Out[7]: 'abc'
In [8]: x.groupdict()
Out[8]: {}
In [9]: re.match("abc", 'zyxabcdre')
match
匹配的是开始,如果开始没有匹配到,就返回None
;而search
可以匹配字符串的中间部分,匹配到一个就返回。
flag
可以给匹配添加更多选项,常见的flag
如下:
3 正则表达式语法
这也是通常说的正则表达式,正则表达式用 Python 字符串实现,它们属于 Python 字符串,正则引擎处理正则表达式时候有额外的语法。
从第二部分知道,大部分字符都匹配它们自己,少数字符可以匹配其它字符串,它们需要被重点关注:
3.1 匹配单个字符
3.1.1 大部分字符匹配自己
匹配大小写敏感,可添加re.IGNORECASE
通配大小写
In [1]: re.search('a', 'ABCD')
In [2]: re.search('a', 'ABCD', re.IGNORECASE)
Out[2]: <re.Match object; span=(0, 1), match='A'>
转义序列也包含其中,但不包含反斜杠
In [1]: re.search('\n', 'ab\ncd')
Out[1]: <re.Match object; span=(2, 3), match='\n'>
对反斜杠的特殊处理
确实存在匹配单个字面意义上的反斜杠的情况,如匹配如下$LaTeX$:
\section{back slash}
若按普通转义序列一样处理反斜杠,即用\\
作为正则表达式,则会报错:
In [1]: re.search("\\", "\\x")
---------------------------------------------------------------------------
error Traceback (most recent call last)
<ipython-input-2-ace9affb99aa> in <module>
----> 1 re.search("\\", "\\x")
/usr/local/lib/python3.10/re.py in search(pattern, string, flags)
199 """Scan through string looking for a match to the pattern, returning
200 a Match object, or None if no match was found."""
--> 201 return _compile(pattern, flags).search(string)
203 def sub(pattern, repl, string, count=0, flags=0):
/usr/local/lib/python3.10/re.py in _compile(pattern, flags)
302 if not sre_compile.isstring(pattern):
303 raise TypeError("first argument must be string or compiled pattern")
--> 304 p = sre_compile.compile(pattern, flags)
305 if not (flags & DEBUG):
306 if len(_cache) >= _MAXCACHE:
/usr/local/lib/python3.10/sre_compile.py in compile(p, flags)
762 if isstring(p):
763 pattern = p
--> 764 p = sre_parse.parse(p, flags)
765 else:
766 pattern = None
/usr/local/lib/python3.10/sre_parse.py in parse(str, flags, state)
940 # parse 're' pattern into list of (opcode, argument) tuples
--> 942 source = Tokenizer(str)
944 if state is None:
/usr/local/lib/python3.10/sre_parse.py in __init__(self, string)
230 self.index = 0
231 self.next = None
--> 232 self.__next()
233 def __next(self):
234 index = self.index
/usr/local/lib/python3.10/sre_parse.py in __next(self)
243 char += self.decoded_string[index]
244 except IndexError:
--> 245 raise error("bad escape (end of pattern)",
246 self.string, len(self.string) - 1) from None
247 self.index = index + 1
error: bad escape (end of pattern) at position 0
原因如下:正则引擎首先对正则表达式处理,把\\
(两个连续反斜杠)翻译为\
(一个反斜杠),此时正则表达式变成\"
(一个反斜杠结合后面的引号),正则表达式缺少字符串结束标志符--引号"
,报错。(正则引擎对正则表达式还会有其它处理,后面会介绍。)正确姿势是用四个反斜杠:
In [1]: re.search("\\\\", "\\subgraph")
Out[1]: <re.Match object; span=(0, 1), match='\\'>
好家伙!为匹配一个字面意义上的反斜杠,需要在正则表达式中用四个连续反斜杠。这也是所谓的反斜杠灾难。为了避免复杂的写法,正则表达式中也有原始字符串(Raw String),作用是:正则引擎处理正则表达式时候,不把连续两个反斜杠\\
翻译为一个反斜杠\
。
In [1]: re.search(r"\\", "\\x")
Out[1]: <re.Match object; span=(0, 1), match='\\'>
为了让正则表达式和 Python 字符串更加接近,一条推荐的规则是:不管多么简单的规则,都采用原始字符串的方式进行匹配。
3.1.2 点.
匹配所有字符
点.
匹配所有字符,这个和通配符中星号*
表示所有字符一样。通常点并不能匹配换行符,加入re.DOTALL
就可以了。
需要注意的是这里说的是单个字符。
3.1.3 []
表示集合
中括号([]
)表示范围,匹配中括号中任意一个元素。中间是或的关系,元素之间不需要隔开。
In [1]: re.search(r'[1234]','843')
Out[1]: <re.Match object; span=(1, 2), match='4'>
中括号中的元素可以重复,但是并没有特殊意义:
In [1]: re.search(r'[ddxs]','d')
Out[1]: <re.Match object; span=(0, 1), match='d'>
中括号中大部分字符(包含转移序列)都只表示原本意思,除了特殊字符:点(.
)可以匹配任意字符,但在中括号([]
)中却只匹配字面上的点(.
):
In [1]: re.search(r'.', ',')
Out[1]: <re.Match object; span=(0, 1), match=','>
In [2]: re.search(r'[.]', ',')
In [3]: re.search(r'[\n]', 'x\n')
Out[3]: <re.Match object; span=(1, 2), match='\n'>
对某些需要转义表示的字符,也可以放到中括号中来避免使用转义符号,比如反斜杠:
In [1]: re.search(r"[\\]", '\\x')
Out[1]: <re.Match object; span=(0, 1), match='\\'>
需要特殊关注的字符是 ^
、 -
、 [
,规则如下:
^
符号表示取反,但是^
一定要添加在最开始
In [1]: re.search(r'[^1234]','1843')
Out[1]: <re.Match object; span=(1, 2), match='8'>
如果^
在中间,则只会被当成是一个普通字符
In [1]: re.search(r'[7^1234]','1843')
Out[1]: <re.Match object; span=(2, 3), match='1'>
中括号中使用-
表示范围,实现对表达式的精简
r"[0-9]" # equal r"[0123456789]" in regular expression
r"[a-z]" # equal r"[abcdefghijklmnopqrstuvwxyz]"in regular expression
需注意若-
前后不是范围,则只会匹配普通的-
符号:
In [1]: re.search("[-a]", "b-")
Out[1]: <re.Match object; span=(1, 2), match='-'>
中括号开始标志[
如果非要表示字面意义上左中括号([
),则要使用\[
:
In [1]: re.search(r"[]]", ']')
Out[1]: <re.Match object; span=(0, 1), match=']'>
In [2]: re.search(r"[[]", ']')
<ipython-input-44-863737df3ef5>:1: FutureWarning: Possible nested set at position 1
re.search(r"[[]", ']')
当要处理(字面意义)中括号时候,最好通通用转义序列。
3.1.4 正则表达式转义序列
它们虽然是反斜杠(\
)和某个字符的组合,但不是Python 转义序列:只有正则引擎会对它们特殊处理,而在其它情况下只会被看成是普通字符串。它们也可看成对中括号([]
)的扩展。如下正则表达式含义比较清楚:
r1 = "[0-9]"
r2 = "[a-zA-Z_]"
r1
表示一位十进制数,r2
表示任意字母数字或下划线。可以用正则表达式转义序列进一步简写:
r1 = "\d"
r2 = "\w"
更多的正则表达式转义序列如下:
前面介绍正则表达式原始字符串时候提到,正则引擎会对首先正则表达式分析,所谓的正则表达式转义字符就是被正则引擎进行处理。
3.2 字符串匹配
实际中更有用的是字符串的匹配。如同字符串对字符的扩展方式,正则表达式并列排布,则匹配并列排布的字符和字符串:
若正则表达式regA
、regB
分别匹配字符chaA
、chaB
,则正则表达式regAregB
匹配字符串chaAchaB
若正则表达式regA
、regB
分别匹配字符串strA
、strB
,则正则表达式regAregB
匹配字符串strAstrB
reg1 = r"\d"
reg2 = r"\d\d"
reg3 = r"\d\w"
re.search(reg1, "56") # 5
re.search(reg2, "56") # 55
re.search(reg3, "5x") # 5x
3.2.1 小括号界定正则表达式范围
并列排布的运算并不都是从左往右计算,而是依据运算的优先级从高到低进行运算。为了说明优先级会影响匹配的结果,这里引入一个优先级极低的运算符号|
,它表示或的关系。
In [1]: reg1, reg2 = r'a', r'b|c'
In [2]: re.match(r'ab|c', 'ac')
In [3]: re.match(r'ab|c', 'c')
Out[3]: <re.Match object; span=(0, 1), match='c'>
In [4]: re.match(r'ab|c', 'ab')
Out[4]: <re.Match object; span=(0, 2), match='ab'>
直接把reg1
和reg2
组合到一起,并不能匹配ac
,只能匹配ab
或者c
了。实际上匹配的优先级被修改。字符组合优先级高于或(|
)运算。此时如果要实现匹配ac
,可以添加小括号(
实现:
In [1]: re.match(r'a(b|c)', 'ac')
Out[1]: <re.Match object; span=(0, 2), match='ac'>
小括号界定正则表达式的范围,改变了匹配优先级。添加非字面意思的小括号并不是匹配小括号的意思。
In [1]: re.search(r'x|y|z', "z")
Out[1]: <re.Match object; span=(0, 1), match='z'>
In [2]: re.search(r'(x)|(y)|(z)', "z")
Out[2]: <re.Match object; span=(0, 1), match='z'>
同时,小括号可以把一个正则表达式界定为一个组(group
)。后续可以对这个组进行操作。下一节中将对组进行说明。
3.2.2 重复匹配
根据前面的规则,下面的正则表达式用于匹配多位十进制数:
\d\d
\d\d\d
\d\d\d\d
如果要匹配位数更多的数,则需要加长正则表达式。显然这样并不方便,于是就有了如下简写:
(REG){m,n} # 至少m次,至多n次
(REG){,n} # 至少0次,至多n次
(REG){m,} # 至少m次
(REG){m} # 只能是m次
特殊情况下可继续简写:
(REG){0,1} -> (REG)?
(REG){1,} -> (REG)+
(REG){0,} -> (REG)*
上面检索都是贪婪的:会尽可能长地匹配。在正则表达式后面加上?
实现非贪婪搜索:
In [1]: re.search(r'm{2,3}', "mmm")
Out[1]: <re.Match object; span=(0, 3), match='mmm'>
In [2]: re.search(r'm{2,3}?', "mmm")
Out[2]: <re.Match object; span=(0, 2), match='mm'>
除了对单个字符这样操作,也可以把用括号,把多个正则表达式作为一组进行操作:
In [1]: re.search(r'(ab)+', 'abababxxx')
Out[1]: <re.Match object; span=(0, 6), match='ababab'>
In [2]: re.search(r'ab+', 'abababxxx')
Out[2]: <re.Match object; span=(0, 2), match='ab'>
1 把ab
看成一个整体,进行重复匹配;2 是匹配a
和多个b
。这里括号让一系列正则表达式组成一个组,然后对整个组进行操作。
3.3 捕获组、非捕获组与命名组
3.3.1 小括号与捕获组
总结一下小括号作用:
界定正则表达式范围
改变匹配的优先级
把一系列正则表达式匹配的结果对应到组(group)中
REG
是一个合法的正则表达式:
(REG)
此时REG
匹配到的结果就会存到一个 group 中,可以通过group
函数进行访问。
In [1]: x = re.search(r'((\d)\w(\d))', "a1b3x")
In [2]: x.group(0)
Out[2]: '1b3'
In [3]: x.group(1)
Out[3]: '1b3'
In [4]: x.group(2)
Out[5]: '1'
In [5]: x.group(3)
Out[5]: '3'
In [6]: x.group(4)
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-29-b8b09168b4e1> in <module>
----> 1 x.group(4)
IndexError: no such group
In [7]: x.group(1, 3)
Out[7]: ('1b3', '3')
以正则表达式最左边为1
,从左往右开始计数,(非转义)左括号((
)排列的顺序就是 group 函数访问时候的参数,此时返回当前左括号和对应右括号中的正则表达式匹配的结果。group()
和group(0)
等价,都表示整个正则表达是匹配的结果。
In [1]: x = re.search(r'\d', '5')
In [2]: x.group(0)
Out[2]: '5'
In [3]: x.group()
Out[3]: '5'
这种用小括号包围正则表达式得到的组就是捕获组(Capture Group)。捕获组的结果会存储到内存中,可以通过 group 的 index 进行访问。
3.3.2 非捕获组
对于组而言,存储匹配结果并且访问并非一定必要:有时仅仅想知道有这么个匹配(并不想知道具体匹配结果),或者说存储结果这个过程占用空间、影响速度而变得不可以。此时就引入非捕获组(Non Capture Group),只表示匹配关系,不存储具体结果:
(?:REG)
非捕获组也使用了小括号()
来界定范围,单非捕获组正则表达式前面会有问号(?
)和冒号(:
)。
问号(?
)来自 Perl(大部分语言的正则表达式实现都受 Perl 影响)。Perl 从 4 升级到 5 时,为了保持兼容性引入了(?
的写法:对一个非转义的左括号((
)用问号(?
)进行重复匹配没有意义,换言之(?
并不会引起歧义;
从另一个角度来看,不会有一个合法的正则表达式以问号(?
)开头。
冒号(:
)表示普通非捕获组,表示匹配当前位置的字符(串)。当然,还有其它非捕获组,如后向断言(lookbehind)和前向断言(lookahead)。
非捕获组不会计入捕获组的 index 中,无论是否同时存在捕获组:
In [1]: x = re.search(r'((?:\d)\w(\d))', "a1b3x")
In [2]: x.group(0,1,2)
Out[2]: ('1b3', '1b3', '3')
In [3]: x.group(3)
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-9-6cd92edb32bb> in <module>
----> 1 x.group(3)
IndexError: no such group
group(2)
匹配最后一个3
,没有group(3)
。非捕获组不计入 group 结果只针对非捕获组所在的括号,但对非捕获组外面或者内部的捕获组并没有影响:
In [1]: x = re.search(r'([a-c])(?:[d-f]([g-h]))','adg')
In [2]: x.group(0,1,2)
Out[2]: ('adg', 'a', 'g')
3.3.3 命名组
有时除了用 index 来访问匹配内容,还希望赋予匹配一个有意义的名字。此时可以用命名组(Named Group),它属于捕获组,主要解决组命名的问题。
(?P<name>REG)
P
表示这是Python 正则表达式的语法(而非来自 Perl)。命名组是一种捕获组,之前捕获组使用 index 来访问 group 的方式依旧有效。命名组在此之外添加了用名字访问的方式,同时会获得一个非空的groupdict
:
In [1]: x = re.search(r'(?P<hh>[a-c])(?:[d-f]([g-h]))','adg')
In [2]: x
Out[2]: <re.Match object; span=(0, 3), match='adg'>
In [3]: x.group(0,1,2)
Out[3]: ('adg', 'a', 'g')
In [4]: x.group('hh')
Out[4]: 'a'
In [5]: x.groupdict()
Out[5]: {'hh': 'a'}
3.3.4 组的应用--匹配重复的字符(串)
如果要匹配连续重复的字符串,则可以用\{num}
,其中{num}
是数字,一定要是一个存在的 index,且不能是0
,这也要求要重复的对象一定要在小括号中。
In [1]: re.search(r'(\d)\1{2}', '21112121211')
Out[1]: <re.Match object; span=(1, 4), match='111'>
上面的\1
中的1
就是 group 的 index。
In [1]: re.search(r'(\d)\0{2}', '21112121211')
In [2]: re.search(r'\d\0{2}', '21112121211')
In [3]: re.search(r'(\d)\2{2}', '21112121211')
---------------------------------------------------------------------------
error Traceback (most recent call last)
<ipython-input-32-86e83c2fdb65> in <module>
----> 1 re.search(r'(\d)\2{2}', '21112121211')
/usr/lib64/python3.9/re.py in search(pattern, string, flags)
199 """Scan through string looking for a match to the pattern, returning
200 a Match object, or None if no match was found."""
--> 201 return _compile(pattern, flags).search(string)
203 def sub(pattern, repl, string, count=0, flags=0):
/usr/lib64/python3.9/re.py in _compile(pattern, flags)
302 if not sre_compile.isstring(pattern):
303 raise TypeError("first argument must be string or compiled pattern")
--> 304 p = sre_compile.compile(pattern, flags)
305 if not (flags & DEBUG):
306 if len(_cache) >= _MAXCACHE:
/usr/lib64/python3.9/sre_compile.py in compile(p, flags)
762 if isstring(p):
763 pattern = p
--> 764 p = sre_parse.parse(p, flags)
765 else:
766 pattern = None
/usr/lib64/python3.9/sre_parse.py in parse(str, flags, state)
947 try:
--> 948 p = _parse_sub(source, state, flags & SRE_FLAG_VERBOSE, 0)
949 except Verbose:
950 # the VERBOSE flag was switched on inside the pattern. to be
/usr/lib64/python3.9/sre_parse.py in _parse_sub(source, state, verbose, nested)
441 start = source.tell()
442 while True:
--> 443 itemsappend(_parse(source, state, verbose, nested + 1,
444 not nested and not items))
445 if not sourcematch("|"):
/usr/lib64/python3.9/sre_parse.py in _parse(source, state, verbose, nested, first)
524 if this[0] == "\\":
--> 525 code = _escape(source, this, state)
526 subpatternappend(code)
/usr/lib64/python3.9/sre_parse.py in _escape(source, escape, state)
421 state.checklookbehindgroup(group, source)
422 return GROUPREF, group
--> 423 raise source.error("invalid group reference %d" % group, len(escape) - 1)
424 if len(escape) == 2:
425 if c in ASCIILETTERS:
error: invalid group reference 2 at position 5
3.4 关系匹配
有时候希望匹配的字符串前面或者后面满足一些条件,但是对条件本身的内容并不关心。此时就要用到关系匹配。
关系匹配也可以认为是零宽度匹配,返回宽度为零的字符串。
3.4.1 开头结尾匹配
^
和$
分别匹配开头和结尾:
In [1]: re.search(r"^c\w", 'cacb')
Out[1]: <re.Match object; span=(0, 2), match='ca'>
In [2]: re.search(r"\w$", 'cacb')
Out[2]: <re.Match object; span=(3, 4), match='b'>
In [3]: re.search(r"https|http","https")
Out[3]: <re.Match object; span=(0, 4), match='http'>
In [4]: re.search(r"(http)|(https)", "https")
Out[4]: <re.Match object; span=(0, 4), match='http'>
更宽泛的条件是边界匹配,可用\b
来表示正则表达式。
3.4.2 后向断言(lookbehind)
实际上要求字符串前面满足一些条件。
这翻译容易让人迷惑,估计这里是说要着重观察后面的结果并且返回。
(:<=REG) # positive
(:<!REG) # negative
In [1]: re.search(r'(?<=\d)abc', 'abc1abcdabc\n')
Out[1]: <re.Match object; span=(4, 7), match='abc'>
In [2]: re.search(r'(?<!\d)abc', 'abc1abcdabc\n')
Out[2]: <re.Match object; span=(0, 3), match='abc'>
In [3]: re.search(r'(?<=c)(?<=\d)abc', 'abc1abcdabc\n')
1 要求前面一定要有一个 \d
;而 2 正好相反,只返回 4~7 处的abc
。
后向断言是一种零宽度匹配,它匹配长度为零的字符(串)。按照前面正则表达式的排列规则,多个正则表达式并列排布表示一起匹配。由于零宽度字符相加还是零款度字符,多个后向断言并列排布,还是匹配一个结果。此时就起到了一个与运算的效果。
In [1]: re.search(r'(?<=c\d)abc', 'abc1abcdabc\n')
Out[1]: <re.Match object; span=(4, 7), match='abc'>
3.4.2 前向断言(lookahead)
后向断言对称操作,表达式后面要满足一定条件。
(?=REG) # positive
(?!REG) # negative
这两种断言也可以联合使用:
In [1]: re.search(r'(?<!\d)abc(?=\d)', 'abc1abcdabc\n')
Out[1]: <re.Match object; span=(0, 3), match='abc'>
3.4.3 用断言来拼凑正则表达式取反
如何用正则表达式来确认一个字符串中没有另外的字符串?
3.5 正则表达式优先级
如同编程语言的运算都有运算优先级一样,正则表达式也有优先级,当正则表达式遇到一起时,就需要考虑其优先级。
下表列出了优先级,从上到下递减,从左到右递减:
\
转义字符
()
, (?:)
, (?=)
, (?!)
,[]
\*
, +
, ?
, {n}
, {n,}
, {n,m}
^
, $
, \任何元字符
、任何字符 定位点和序列(即:位置和顺序)
|
"或"操作字符
4 其它函数
4.1 re.findall
找到所有匹配
re.findall
返回所有匹配结果组成的列表:
In [1]: re.findall(r'[a-z][A-Z]', 'aAxxxxxxbBxxxxxx')
Out[1]: ['aA', 'bB']
In [2]: re.findall(r'([a-z])([A-Z])', 'aAxxxxxxbBxxxxxx')
Out[2]: [('a', 'A'), ('b', 'B')]
若正则表达中没有捕获组,返回列标的元素是整个匹配(group()
或group(0)
),如上面的 1;如果有捕获组,则列表元素是所有 group 组成的 tuple,如上面的 2。
4.2 re.split
分割字符串
re.split
用于对字符串进行切片。比 Python 自带的str.split
功能更加强大。
re.split(reg, string, maxsplit=0)
maxsplit
的意思是最多切分次数,默认值0
表示所有都切分。
In [1]: re.split(r"\d", "20python21")
Out[1]: ['', '', 'python', '', '']
In [2]: re.split(r"\d", "20python21", 1)
Out[2]: ['', '0python21']
split
的结果包含空字符串。
4.3 re.sub
替换字符串
re.sub
用于匹配对象替换,sub
是 substring 的意思。
re.sub(reg, repl, string)
其中repl
是替换方式,可以是固定字符串、函数或者lambda
表达式,对于后面两种情况,操作的对象是match group
:
In [1]: re.sub(r'\d', 'NUM', "hello world 5")
Out[1]: 'hello world NUM'
In [1]: re.sub(r'\d', lambda x: str(int(x.group()) + 3), "hello world 5")
Out[1]: 'hello world 8'
字符串替换函数replace
替换对象只能是固定的字符串,而正则表达式扩展了替换的对象。多次调用replace
的结果并不一定和re.sub
等价。
和sub
相似的函数是subn
,返回一个 tuple,两个元素依次是替换后的字符串和替换次数: