一、正则表达式算法
正则表达式(或 RE)指定了一组与之匹配的字符串;模块内的函数可以检查某个字符串是否与给定的正则表达式匹配(或者正则表达式是否匹配到字符串,这两种说法含义相同)。
二、正则表达式语法
正则表达式默认贪婪匹配;要求更严格的匹配要求放在'|'左边
1、普通字符
正则表达式可以包含普通或者特殊字符。绝大部分普通字符,比如 'A'
, 'a'
, 或者 '0'
,都是最简单的正则表达式。它们就匹配自身。你可以拼接普通字符,所以 last
匹配字符串 'last'
.
- 字母:如
a-z
,A-Z
。 - 数字:如
0-9
。 - 特殊字符:如
!@#$%^&*()
当它们不被用作元字符时。
字符集
普通字符可以与字符集配合使用:字符集中除预定义的字符外,默认是普通字符含义。
字符集仅表示可以匹配1个在字符集的字符,但要多个要用量词匹配。
字符集的字符没有先后顺序要求,[1-9-]+ 可匹配‘-123456-’
-
字符可以单独列出,比如
[amk]
匹配'a'
,'m'
, 或者'k'
。
-
可以表示字符范围,通过用
'-'
将两个字符连起来。比如[a-z]
将匹配任何小写ASCII字符,[0-5][0-9]
将匹配从00
到59
的两位数字,[0-9A-Fa-f]
将匹配任何十六进制数位。 如果-
进行了转义 (比如[a\-z]
)或者它的位置在首位或者末尾(如[-a]
或[a-]
),它就只表示普通字符'-'
。 -
特殊字符在集合中会失去其特殊意义。比如
[(+*)]
只会匹配这几个字面字符之一'('
,'+'
,'*'
, or')'
。
-
字符类如
\w
或者\S
(定义如下) 也在集合内被接受,不过它们可匹配的字符则依赖于所使用的 flags。
-
不在集合范围内的字符可以通过 取反 来进行匹配。如果集合首字符是
'^'
,所有 不 在集合内的字符将会被匹配,比如[^5]
将匹配所有字符,除了'5'
,[^^]
将匹配所有字符,除了'^'
.^
如果不在集合首位,就没有特殊含义。 -
要在集合内匹配一个
']'
字面值,可以在它前面加上反斜杠,或是将它放到集合的开头。 例如,[()[\]{}]
和[]()[{}]
都可以匹配右方括号,以及左方括号,花括号和圆括号。
转普通字符
如果要匹配 '|'
字符,使用 \|
, 或者把它包含在字符集里,比如 [|]
.
2、元字符
元字符是指在正则表达式中有特殊含义的字符。它们通常用于描述匹配模式的行为:
预定义字符类
预定义字符类提供了一些常用的字符集:
\d
:任何十进制数字[0-9]
。\D
:非数字字符[^0-9]
。\w
:任何字母数字字符[a-zA-Z0-9_]
。\W
:非字母数字字符[^a-zA-Z0-9_]
。\s
:空白字符(空格、制表符、换页符等)。\S
:非空白字符。
按功能区分的特殊字符
- 定位符:用于指明匹配的位置。
^
:匹配输入字符串的开始位置。$
:匹配输入字符串的结束位置。
- 量词:控制前面的元素重复的次数。
*
:前面的元素可以出现零次或多次。+
:前面的元素至少出现一次。?
:前面的元素可以出现零次或一次。{n}
:前面的元素恰好出现 n 次。{n,}
:前面的元素至少出现 n 次。{m,n}
:前面的元素至少出现 m次,但不超过 n 次。_在 m 和 n 之间取尽量多,默认优先匹配n次
- 非贪婪量词:非贪婪量词会尽可能少地匹配字符。
*?: 非贪婪的 `*`。
+?: 非贪婪的 `+`。
??: 非贪婪的 `?`。
{m,n}?: 非贪婪的 `{m,n}` ——尝试匹配尽可能 少的 重复次数,默认优先匹配m次
- 字符集:定义一个字符集。
[]
:定义一个字符集合。[^]
或[:^:]
:定义一个否定的字符集合。
- 选择:表示多个可能的匹配。
|
:选择操作符,表示“或”关系。- 扫描目标字符串时,
'|'
分隔开的正则样式从左到右进行匹配。当一个样式完全匹配时,这个分支就被接受。意思就是,一旦 A 匹配成功, B 就不再进行匹配,即便它能产生一个更好的匹配。或者说,'|'
操作符绝不贪婪。
- 分组与引用:用于组合子表达式,并允许对子表达式的引用。
()
:创建一个捕获组。\1
,\2
, ...:引用第 n 个捕获组的内容。
- 其他特殊字符: . 匹配任何单个字符(除了换行符)。
- 转义字符:用于表示特殊字符或预定义字符类。
\
:转义字符,后跟一个特殊字符或预定义字符类。
- 断言:测试一个位置而非匹配字符。
(?=...)
:正向先行断言,检查后面是否跟着...
。(?!...)
:负向先行断言,检查后面是否不是...
。(?<=...)
:正向后行断言,检查前面是否是...
。(?<!...)
:负向后行断言,检查前面是否不是...
。
- 边界符:用于匹配单词边界。
\b
:单词边界。\B
:非单词边界。
特殊用途字符
- 控制字符:如
\t
(制表符),\n
(换行符)等。
三、re函数
Python 基于正则表达式提供了不同的原始操作:
-
re.match() 只在字符串的开头位置检测匹配。
-
re.search() 在字符串中的任何位置检测匹配(这也是 Perl 在默认情况下所做的)
-
re.fullmatch() 检测整个字符串是否匹配
re.findall(pattern, string, flags=0)
- 用途:在字符串中查找所有与正则表达式匹配的子串。
- 语法:
re.findall(pattern, string, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。string
:要搜索的目标字符串。flags
:可选参数,用于设置正则表达式的标志位。
- 返回值:返回一个包含所有匹配项的列表。
- 返回值类型:
list
。
re.sub(pattern, repl, string, count=0, flags=0)
- 用途:替换字符串中所有与正则表达式匹配的子串。
- 语法:
re.sub(pattern, repl, string, count=0, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。repl
:替换字符串或一个回调函数。string
:要搜索的目标字符串。count
:可选参数,指定最多替换的匹配次数。flags
:可选参数,用于设置正则表达式的标志位。
- 返回值:返回替换后的字符串。
- 返回值类型:
str
。
re.split(pattern, string, maxsplit=0, flags=0)
- 用途:根据正则表达式分割字符串。
- 语法:
re.split(pattern, string, maxsplit=0, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。string
:要分割的目标字符串。maxsplit
:可选参数,指定最多分割的次数。默认为0
,表示分割所有匹配项。flags
:可选参数,用于设置正则表达式的标志位。
- 返回值:返回一个由分割后的子字符串组成的列表。
- 返回值类型:
list
。
re.search(pattern, string, flags=0)
- 用途:搜索字符串中是否存在与正则表达式匹配的子串。
- 语法:
re.search(pattern, string, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。string
:要搜索的目标字符串。flags
:可选参数,用于设置正则表达式的标志位,例如re.IGNORECASE
表示忽略大小写。
- 返回值:如果找到匹配项,则返回第一个匹配的对象;否则返回
None
。 - 返回值类型:
re.Match
类型的对象或者None
。
re.match(pattern, string, flags=0)
- 用途:从字符串的起始位置匹配正则表达式。
- 语法:
re.match(pattern, string, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。string
:要搜索的目标字符串。flags
:可选参数,用于设置正则表达式的标志位。
- 返回值:如果匹配成功,则返回一个匹配对象;否则返回
None
。 - 返回值类型:
re.Match
类型的对象或者None
。
import re
p = '[a-z]+'
s = '123abc1a'
print(re.split(p,s)) # ['123', '1', '']
print(re.findall(p, s)) # ['abc', 'a']
print(re.match(p,s)) # None,起始位置
print(re.search(p,s)) # <re.Match object; span=(3, 6), match='abc'>
print(re.search(p,s).group()) # abc 要获取Match对象的值,需要用group
print(re.sub(p,'*',s)) # 123*1*
r = re.compile(p)
print(r.findall(s)) # ['abc', 'a']
re.finditer(pattern, string, flags=0)
- 用途:返回一个迭代器,该迭代器会产生每个匹配项的匹配对象。
- 语法:
re.finditer(pattern, string, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。string
:要搜索的目标字符串。flags
:可选参数,用于设置正则表达式的标志位。
- 返回值:返回一个迭代器,可用于遍历所有匹配项。
- 返回值类型:
iterator
。
re.compile(pattern, flags=0)
- 用途:编译正则表达式为模式对象,可以用于多次匹配。
- 语法:
re.compile(pattern, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。flags
:可选参数,用于设置正则表达式的标志位。
- 返回值:返回一个模式对象,可以使用它的
search
,match
,findall
,finditer
, 和sub
方法。 - 返回值类型:
re.Pattern
类型的对象。
re.fullmatch(pattern, string, flags=0)
- 用途:在整个字符串中匹配正则表达式。
- 语法:
re.fullmatch(pattern, string, flags=0)
- 参数:
pattern
:要匹配的正则表达式字符串。string
:要搜索的目标字符串。flags
:可选参数,用于设置正则表达式的标志位。
- 返回值:如果整个字符串与正则表达式完全匹配,则返回一个匹配对象;否则返回
None
。
四、re对象
class re.Pattern
由 re.compile() 返回的已编译正则表达式对象。
创建 re.Pattern
对象
- 函数:
re.compile(pattern, flags=0)
- 参数:
pattern
:正则表达式的字符串形式。flags
:可选参数,用于设置正则表达式的标志位,如re.IGNORECASE
等。
使用 re.Pattern
对象
一旦有了re.Pattern
对象,就可以使用它的方法来进行匹配操作:
search(string, pos=0, endpos=None)
:搜索字符串中是否存在与正则表达式匹配的子串。- 参数:
string
:要搜索的目标字符串。pos
:可选参数,指定搜索的起始位置。endpos
:可选参数,指定搜索的结束位置。
- 返回值:如果找到匹配项,则返回一个匹配对象;否则返回
None
。
- 参数:
match(string, pos=0, endpos=None)
:从字符串的起始位置匹配正则表达式。- 参数:
string
:要搜索的目标字符串。pos
:可选参数,指定搜索的起始位置。endpos
:可选参数,指定搜索的结束位置。
- 返回值:如果匹配成功,则返回一个匹配对象;否则返回
None
。
- 参数:
fullmatch(string, pos=0, endpos=None)
:在整个字符串中匹配正则表达式。- 参数:
string
:要搜索的目标字符串。pos
:可选参数,指定搜索的起始位置。endpos
:可选参数,指定搜索的结束位置。
- 返回值:如果整个字符串与正则表达式完全匹配,则返回一个匹配对象;否则返回
None
。
- 参数:
findall(string, pos=0, endpos=None)
:在字符串中查找所有与正则表达式匹配的子串。- 参数:
string
:要搜索的目标字符串。pos
:可选参数,指定搜索的起始位置。endpos
:可选参数,指定搜索的结束位置。
- 返回值:返回一个包含所有匹配项的列表。
- 参数:
finditer(string, pos=0, endpos=None)
:返回一个迭代器,该迭代器会产生每个匹配项的匹配对象。- 参数:
string
:要搜索的目标字符串。pos
:可选参数,指定搜索的起始位置。endpos
:可选参数,指定搜索的结束位置。
- 返回值:返回一个迭代器,可用于遍历所有匹配项。
- 参数:
sub(repl, string, count=0, pos=0, endpos=None)
:替换字符串中所有与正则表达式匹配的子串。- 参数:
repl
:替换字符串或一个回调函数。string
:要搜索的目标字符串。count
:可选参数,指定最多替换的匹配次数。pos
:可选参数,指定搜索的起始位置。endpos
:可选参数,指定搜索的结束位置。
- 返回值:返回替换后的字符串。
- 参数:
可以使用上面的函数,如下:
r = re.compile(p)
print(r.findall(s)) # ['abc', 'a']
class re.Match
由成功的 match
和 search
所返回的匹配对象。
使用 re.Match
对象
re.Match
对象提供了以下方法来访问匹配结果:
group([group1, ...])
:返回一个或多个分组匹配的字符串。如果没有分组,则返回整个匹配的字符串。- 参数:
group1, ...
:可选参数,指定要返回的分组编号。
- 返回值:匹配的字符串或元组。
- 参数:
groups(default=None)
:返回一个包含所有非空分组的元组。- 参数:
default
:可选参数,如果分组为空则返回此默认值。
- 返回值:一个包含所有非空分组的元组。
- 参数:
start([group])
:返回匹配开始的位置。- 参数:
group
:可选参数,指定要返回开始位置的分组编号。
- 返回值:匹配开始的位置。
- 参数:
end([group])
:返回匹配结束的位置。- 参数:
group
:可选参数,指定要返回结束位置的分组编号。
- 返回值:匹配结束的位置。
- 参数:
span([group])
:返回一个元组,包含匹配的开始和结束位置。- 参数:
group
:可选参数,指定要返回位置的分组编号。
- 返回值:一个元组,包含匹配的开始和结束位置。
- 参数:
re
:返回用于匹配的正则表达式对象。string
:返回原始的搜索字符串。lastgroup
:返回最后一个匹配的分组编号。lastindex
:返回最后一个匹配的分组索引。
五、分组
分组匹配的概念
在正则表达式中,可以使用圆括号 ()
来定义一个分组。一个分组内的表达式作为一个整体参与匹配过程,并且可以通过分组编号来引用该分组匹配到的内容。
基本用法
- 创建分组:使用圆括号
()
将需要作为一个整体匹配的部分括起来。 - 引用分组:使用反斜杠
\
后跟一个数字来引用分组。数字表示分组的编号,从左到右依次递增,第一个分组编号为 1。
'''如果你需要找到所有符合分组要求的匹配项,应该使用 re.findall。
如果你需要从字符串的开头进行精确匹配,并且只需要第一个匹配项,可以使用 re.match。
如果你需要在字符串中找到第一个匹配项,无论它出现在什么位置,可以使用 re.search。'''
import re
s = 'my birth is 2024-8-3, xixi is 2022-4-2'
print(re.findall('\d+',s)) # ['2024', '8', '3', '2022', '4', '2']
print(re.findall('\d+-\d+-\d+',s)) # ['2024-8-3', '2022-4-2']
print(re.findall('(\d+)-(\d+)-(\d+)',s)) # [('2024', '8', '3'), ('2022', '4', '2')] 二维 最低层是找到的单元组
m = re.match('(\d+)-(\d+)-(\d+)',s) # match必需匹配开头,且只找1个
print(m) # None
# print(m.group()) # AttributeError: 'NoneType' object has no attribute 'group'
m = re.search('(\d+)-(\d+)-(\d+)',s) # search 只找到第一个匹配
print(m) # <re.Match object; span=(0, 8), match='2024-8-3'>
print(m.group()) # 2024-8-3
print(m.group(1)) # 2024
print(m.group(2)) # 8
print(m.groups()) # ('2024', '8', '3')
示例
假设我们要从一个字符串中提取日期格式为 YYYY-MM-DD
的日期,我们可以使用以下正则表达式:
1import re
2
3date_pattern = r'(\d{4})-(\d{2})-(\d{2})'
4text = 'Today is 2023-08-03.'
5
6# 使用 findall 获取所有匹配的日期
7matches = re.findall(date_pattern, text)
8print(matches) # 输出: [('2023', '08', '03')]
在这个例子中,我们定义了三个分组:
- 第一个分组:
(\d{4})
- 匹配四位数字,表示年份。 - 第二个分组:
(\d{2})
- 匹配两位数字,表示月份。 - 第三个分组:
(\d{2})
- 匹配两位数字,表示日期。
使用 re.Match
对象访问分组
当你使用 re.search
, re.match
, re.finditer
等方法时,返回的 re.Match
对象可以用来访问分组匹配的结果。
使用 group()
方法
group()
:返回整个匹配的字符串。group(n)
:返回第 n 个分组匹配的字符串。group(0)
:返回整个匹配的字符串。group(1, 2, ...)
:返回多个分组匹配的字符串作为元组。
使用 groups()
方法
groups()
:返回所有非空分组匹配的字符串组成的元组。groups(default)
:返回所有非空分组匹配的字符串组成的元组,对于未匹配的分组,使用default
作为占位符。
使用 span()
和 start()
, end()
方法
span()
:返回一个元组,包含匹配的开始和结束位置。span(n)
:返回第 n 个分组匹配的开始和结束位置。start()
:返回匹配的开始位置。start(n)
:返回第 n 个分组匹配的开始位置。end()
:返回匹配的结束位置。end(n)
:返回第 n 个分组匹配的结束位置。
示例
下面是一个使用 re.Match
对象访问分组的示例:
1import re
2
3date_pattern = r'(\d{4})-(\d{2})-(\d{2})'
4text = 'Today is 2023-08-03.'
5
6# 使用 search 查找第一个匹配
7match = re.search(date_pattern, text)
8if match:
9 print("Full date:", match.group()) # 输出: Full date: 2023-08-03
10 print("Year:", match.group(1)) # 输出: Year: 2023
11 print("Month:", match.group(2)) # 输出: Month: 08
12 print("Day:", match.group(3)) # 输出: Day: 03
13 print("Year, Month, Day:", match.groups()) # 输出: Year, Month, Day: ('2023', '08', '03')
使用分组进行替换
在使用 re.sub
进行替换时,也可以使用分组来构建新的字符串。
示例
假设我们需要将日期格式从 YYYY-MM-DD
转换为 DD-MM-YYYY
:
1import re
2
3date_pattern = r'(\d{4})-(\d{2})-(\d{2})'
4text = 'Today is 2023-08-03.'
5
6# 使用 sub 替换日期格式
7new_text = re.sub(date_pattern, r'\3-\2-\1', text)
8print(new_text) # 输出: Today is 03-08-2023.
在这个例子中,我们使用 \3-\2-\1
来引用分组,并按照新的顺序重新构造日期。
总结
- 使用圆括号
()
创建分组。 - 使用
group()
和groups()
访问分组匹配的结果。 - 使用
span()
,start()
, 和end()
获取匹配的位置信息。 - 使用分组进行替换时,可以通过反斜杠
\
后跟分组编号来引用分组。
附录: