1. 正则表达式的作用
(1)文本搜索和匹配:可以用来搜索、匹配和替换特定模式的文本。
比如,查找所有符合特定格式的邮箱地址、电话号码等。
(2)数据验证:可以用来验证用户输入是否符合特定的格式要求。
比如,验证电子邮件地址、密码复杂度等。
(3)数据提取:可以从复杂的文本中提取出需要的信息。
比如,从网页源码中抽取出所有链接地址。
(4)文本处理:可以用来进行文本的分割、替换、删除等操作。
比如,删除所有的空格或者特定标记。
(5)语法分析和编译:在编译器和解释器中,正则表达式可以用来识别和处理语法结构。
比如,编程语言中的词法分析阶段,识别关键字、变量名等。
(6)字符串操作:可以用来处理和操作字符串,进行复杂的模式匹配和处理。
比如,对文本进行格式化、规范化等操作。
总之,正则表达式在处理需要模式匹配和字符串操作的场景中非常有用,能够提高处理字符串的效率和灵活性。
2. 正则表达式语法
正则表达式(Regular Expression)的语法主要分为普通字符、元字符。
2.1 普通字符
在正则表达式中,普通字符指的是除了元字符和转义字符以外的任意字符,包括字母、数字、标点符号、空格等。
普通字符在正则表达式中的匹配方式是按照字符的字面意义(表示其本身意义)进行匹配。
以下是一些常见的正则表达式中的普通字符及其含义:
-
字母和数字:代表匹配该字母或数字本身。
-
空格和标点符号:代表匹配该空格或标点符号本身。
-
下划线(
_
):代表匹配下划线本身。下划线(_
):代表匹配下划线本身。
2.2 元字符
元字符是正则表达式中的特殊字符,用来表示特定的字符或字符组合,用来表示特殊的匹配含义。
包括:
代码 | 功能 |
---|---|
.(英文点号) | 匹配任意1个字符(除了\n换行符)。 例如,正则表达式 a.b 可以匹配 aab 、acb 、a4b ,但是不匹配 a\nb 。 |
^(脱字符) | 匹配以某个字符串开头。 例如,正则表达式 ^hello 可以匹配 hello world 中的 hello ,但是不匹配 world hello 中的 hello 。 |
$(美元符号) | 匹配以某个字符串结尾。 例如,正则表达式 world$ 可以匹配 hello world 中的 world ,但是不匹配 world hello 中的 world 。 |
*(星号) | 匹配前一个字符出现0次或者无限次,即可有可无(0到多,任意次)。 例如,正则表达式 ab*c 可以匹配 ac 、abc 、abbc ,但是不匹配 a 或 abcc 。 |
+(加号) | 匹配前一个字符出现1次或者无限次,即至少有1次(1到多,一次以上)。 例如,正则表达式 ab+c 可以匹配 abc 、abbc ,但是不匹配 ac 或 a 。 |
?(问号) | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有(0或1)。 例如,正则表达式 ab?c 可以匹配 ac 或 abc ,但是不匹配 abbc 。 |
|(竖杠) | 选择匹配符,表示或,匹配两边任意一个表达式,可以匹配多个规则。 例如,正则表达式 hello\|world 可以匹配 hello 或 world 。 |
()(英文小括号) | 用于分组,或叫子表达式,可以更精确地控制匹配。 例如,正则表达式 (ab)+ 可以匹配 ab 、abab ,只有由 "ab" 组成的字符串才能匹配 (ab)+ ,且可以重复匹配多个 "ab" 形成的字符串。 |
[](方括号) | 用于指定字符集,匹配方括号中出现的任意一个字符。 例如,正则表达式 [abc] 可以匹配 a 、b 或 c ,但是不匹配 d 或 ab 。 |
[^指定字符] | 匹配除了指定字符以外的其他某个字符,^在字符集里表示取反。 |
{m} | 匹配前一个字符出现m次。例如,匹配手机号码\d{11} 。 |
{m,} | 匹配前一个字符至少出现m次。例如,\w{3,} 代表前面这个字符最少要出现3次,最多可以是无限次。 |
{m,n} | 匹配前一个字符出现从m到n次。例如,\w{6,10} 代表前面这个字符出现6到10次。 |
\ | 用于转义,表示后面的字符不是特殊字符。 |
2.3 常用字符集
基于元字符,有一些常用的字符集。
字符集是由一组字符组成的集合,用方括号 []
表示,用于匹配方括号中出现的任意一个字符。
例如:
代码 | 功能 |
---|---|
[abc] | 表示匹配a、b、c中的任意一个字符。 |
[a-z] | 表示匹配a到z之间的任意一个小写字母。 |
[A-Z] | 表示匹配A到Z之间的任意一个大写字母。 |
[0-9] | 表示匹配0到9之间的任意一个数字。 |
[^abc] | 表示匹配除了a、b、c之外的任意一个字符,^在字符集里表示取反。 |
[0-9a-zA-Z] | 表示匹配0-9之间、a-z之间、A-Z之间的任意某个字符。 |
在字符集中,还可以使用特殊字符来表示一些常用字符集。
例如:
代码 | 功能 |
---|---|
\d | 匹配数字,即[0-9] 。 |
\D | 匹配非数字,即不是数字,[^0-9] 。 |
\s | 匹配空白,即 空格,tab键(制表符),换行符。 |
\S | 匹配非空白。 |
\w | 匹配非特殊字符,匹配任意一个字母、数字或下划线,相当于[a-zA-Z0-9_] 。 |
\W | 匹配特殊字符,匹配任意一个非字母、数字或下划线,相当于[^a-zA-Z0-9_] 。 |
3. re模块
re
模块是 Python 中用于正则表达式操作的标准库,提供了一组用于处理字符串匹配、查找、替换等操作的函数和方法。
在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用re模块。
3.1 re模块使用
# 第一步:导入re模块
import re
# 第二步:使用match方法进行匹配操作
result = re.match(pattern正则表达式, string要匹配的字符串, flags=0)
# 第三步:如果数据匹配成功,使用group方法来提取数据
result.group()
match函数参数说明:
参数 | 描述 |
---|---|
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志 |
说明:
-
匹配成功re.match方法返回一个匹配的对象,否则返回None。
-
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配数据。
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配,这个功能是为了支持多语言版本的字符集使用环境的,比如在转义符\w,在英文环境下,它代表[a-zA-Z0-9 _],即所以英文字符和数字。如果在一个法语环境下使用,缺省设置下,不能匹配"é" 或 "ç"。加上这L选项和就可以匹配了。不过这个对于中文环境似乎没有什么用,它仍然不能匹配中文字符。 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | VERBOSE,冗余模式, 此模式忽略正则表达式中的空白和#号的注释,例如写一个匹配邮箱的正则表达式。该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
3.2 re模块的相关方法
(1)re.match(pattern, string, flags=0)
用于从字符串的开头匹配正则表达式。
如果匹配成功,则返回一个匹配对象;否则返回 None。
可选参数 flags 用于控制正则表达式的匹配方式,后面的几个方法中的flags参数都是一样的作用,常用的有如下几种:
-
re.I 或 re.IGNORECASE:I代表Ignore,忽略大小写。
-
re.M 或 re.MULTILINE:多行匹配,改变
^
和$
的行为。 -
re.S 或 re.DOTALL:让
"."
匹配包括换行符在内的所有字符。 -
re.X 或 re.VERBOSE:忽略空格和注释,让正则表达式更易读。
示例:
import re
# 匹配以数字开头的字符串
result = re.match(r'\d+', '123abc')
if result:
print(result.group()) # 输出: 123
else:
print('No match')
(2)re.search(pattern, string, flags=0)
用于在字符串中查找第一个匹配正则表达式的位置。
如果匹配成功,则返回一个匹配对象;否则返回 None。
示例:
import re
# 查找第一个匹配的数字
result = re.search(r'\d+', 'I have 2 apples and 3 oranges')
if result:
print(result.group()) # 输出: 2
else:
print('No match')
(3)re.findall(pattern, string, flags=0)
用于在字符串中查找所有匹配正则表达式的位置,并返回一个匹配结果的列表。
示例:
import re
# 查找所有匹配的数字
result = re.findall(r'\d+', 'I have 2 apples and 3 oranges')
print(result) # 输出: ['2', '3']
(4)re.finditer(pattern, string, flags=0)
是 re 模块中的函数,用于搜索字符串中与正则表达式匹配的所有子串,并返回一个迭代器。
功能与上面findall一样,不过返回的是迭代器。
函数语法:
re.finditer(pattern, string, flags=0)
参数说明:
-
pattern
:正则表达式模式。 -
string
:要被搜索的字符串。 -
flags
:正则表达式使用时的标志位。
返回值:返回一个迭代器,包含所有匹配的子串。
用法示例:
import re
string = "Hello 123 World 456"
pattern = r'\d+'
for match in re.finditer(pattern, string):
print(match.group())
输出结果:
说明:
-
正则表达式
r'\d+'
用于匹配字符串中的数字字符。 -
re.finditer()
函数返回一个迭代器,包含所有匹配的子串,我们可以使用for
循环遍历迭代器,并通过match.group()
方法获取匹配到的字符串。
(5)re.sub(pattern, repl, string, count=0, flags=0)
re.sub()
是re
模块中的一个函数,用于在字符串中执行替换操作。
其中各参数的含义如下:
-
pattern
:表示正则表达式的模式字符串。 -
repl
:表示替换的字符串或可调用对象。如果是字符串,那么将使用该字符串替换匹配到的内容;如果是可调用对象,则将匹配到的内容作为参数传递给该对象,然后使用其返回的结果进行替换。 -
string
:表示要进行替换操作的字符串。 -
count
:表示替换的最大次数。如果不指定该参数,那么将替换所有匹配到的内容。 -
flags
:表示正则表达式的匹配模式。
re.sub()
函数返回替换后的字符串。
例:
import re
s = 'Hello, World!'
s = re.sub('Hello', 'Hi', s)
print(s)
运行结果:
(6)re.compile(pattern, flags=0)
用于编译正则表达式,并返回一个正则表达式对象。
可选参数 flags 是一个位掩码,用于控制正则表达式的匹配方式。
当需要重复使用同一正则表达式时,可以先使用 re.compile() 进行编译,然后使用返回的正则表达式对象进行匹配。
例:
import re
# 编译正则表达式
pattern = re.compile(r'\d+')
# 使用编译后的正则表达式对象进行匹配
result = pattern.findall('I have 2 apples and 3 oranges')
print(result)
运行结果:
这些函数和方法是在处理正则表达式时经常使用的,通过这些函数和方法,可以方便地对字符串进行模式匹配和替换操作。
4. 重要概念说明
4.1 子表达式(分组)
在Python的正则表达式中,子表达式是指使用小括号 ()
包裹起来的一部分表达式。
它们的作用主要有两个:
-
分组:子表达式可以将多个元素组成一个逻辑分组,以便于在匹配过程中进行重复操作、分析或引用。例如,
(abc){2}
表示匹配两个连续的 "abc"。 -
捕获:子表达式还可以被捕获,以便于在匹配成功后获取到具体的值。捕获的内容可以通过编号或者使用命名分组来引用。
例:
import re
# 分组示例
pattern1 = r'(ab)+'
text1 = 'ababab'
matches1 = re.findall(pattern1, text1)
print(matches1) # 输出: ['ab', 'ab', 'ab']
# 捕获示例
pattern2 = r'(\d{3})-(\d{3})-(\d{4})'
text2 = '123-456-7890'
matches2 = re.search(pattern2, text2)
print(matches2.groups()) # 输出: ('123', '456', '7890')
说明:
-
第一个示例使用了分组
(ab)+
来匹配连续出现的 "ab",结果为['ab', 'ab', 'ab']
。 -
第二个示例使用了捕获的技巧来匹配电话号码的区号、中间号码和尾号,捕获的结果通过
groups()
方法获取,结果为('123', '456', '7890')
。 -
需要注意的是,使用子表达式时可能会增加正则表达式的复杂度,尤其是在多层嵌套或者涉及到回溯的情况下。
-
在编写复杂的正则表达式时,建议合理使用子表达式、量词和逻辑运算符等技巧,以提高正则表达式的性能和可读性。
例:
re.search(r'\d(\d)(\d)', 'abcdef123ghijklmn')
说明:
-
Python正则表达式前的
r
表示原生字符串(rawstring),该字符串声明了引号中的内容表示该内容的原始含义,避免了多次转义造成的反斜杠困扰。 -
正则表达式中
\d(\d)(\d)
中,(\d)(\d)
就是子表达式,一共有两个()
圆括号,则代表两个子表达式。
4.2 捕获
捕获指的是在正则表达式中使用小括号 ()
将匹配的部分包裹起来,以便后续引用或处理这部分内容。捕获的内容可以使用编号或命名分组进行引用。
在正则表达式的匹配过程中,当一个子表达式匹配到相应的内容后,计算机系统会自动将匹配到的内容保存在一个特定的数据结构中,通常称为捕获组的缓存区。
在 Python 中,通过 re
模块进行正则表达式的匹配,可以通过 group(n)
方法来访问捕获的内容,其中 n
为捕获组的编号。
捕获的语法格式如下:
(pattern)
其中,pattern
是用于匹配的正则表达式模式。
例如,假设有如下的正则表达式:
pattern = r'(\d{2})-(\d{2})-(\d{4})'
当这个正则表达式用于匹配字符串 "31-12-2022"
时,系统会自动将 "31"
放入 $1
缓存区,将 "12"
放入 $2
缓存区,将 "2022"
放入 $3
缓存区。
然后,我们可以使用 group(n)
方法来访问缓存区中的内容,如下所示:
match = re.search(pattern, '31-12-2022')
print(match.group(1)) # 输出: 31
print(match.group(2)) # 输出: 12
print(match.group(3)) # 输出: 2022
需要注意的是,缓存区的编号从 1 开始。在正则表达式中,可以有多个子表达式和多个捕获组,系统会自动为每个捕获组分配一个编号。
4.3 命名分组
命名分组是在正则表达式中为捕获组添加名称的一种方式。
它能够使得引用和处理捕获的内容更加清晰和直观。
命名分组的语法格式如下:
(?P<name>pattern)
其中,name
是你希望为该捕获组指定的名称,pattern
是该捕获组的正则表达式模式。
下面是一个使用命名分组的详细说明和案例:
import re
# 使用命名分组匹配手机号码
pattern = r'(?P<area>\d{3})-(?P<exch>\d{3})-(?P<ext>\d{4})'
text = '123-456-7890'
match = re.search(pattern, text)
# 获取命名分组的内容
print(match.group('area')) # 输出: 123
print(match.group('exch')) # 输出: 456
print(match.group('ext')) # 输出: 7890
在上述代码中,我们使用了命名分组来匹配手机号码。具体解释如下:
-
(?P<area>\d{3})
:定义了一个名为 "area" 的命名分组,匹配3位数字。 -
(?P<exch>\d{3})
:定义了一个名为 "exch" 的命名分组,匹配3位数字。 -
(?P<ext>\d{4})
:定义了一个名为 "ext" 的命名分组,匹配4位数字。
然后,通过 group('name')
方法可以引用这些命名分组的内容。
使用命名分组能够提高正则表达式的可读性和可维护性,特别适用于复杂匹配的情况。在一个正则表达式中可以有多个命名分组,并且每个命名分组的名称应该是唯一的。
需要注意的是,命名分组只在re
模块中的search()
、match()
、findall()
等方法中才能使用。
4.4 反向引用
反向引用是正则表达式中的一种功能,它允许引用先前捕获的内容。通过反向引用,可以在正则表达式中使用前一个捕获组所匹配到的内容。
反向引用的语法格式如下:
\N
其中,\N
是一个特殊的转义序列,表示引用编号为 N
的捕获组的内容。
下面是一个使用反向引用的详细说明和案例:
import re
# 使用反向引用匹配连续相同字符
pattern = r'(\w)\1+'
text = 'Hello! Gooddd morninng!!'
matches = re.findall(pattern, text)
# 输出匹配到的连续相同字符
for match in matches:
print(match)
在上述代码中,我们使用了反向引用来匹配连续相同的字符。具体解释如下:
-
(\w)
:匹配一个字母、数字或下划线,并进行捕获。 -
\1+
:匹配一个或多个与第一个捕获组匹配的内容。
这里的 \1
就是反向引用,表示引用编号为 1 的捕获组,即第一个捕获组。
通过 findall()
方法找到所有匹配的结果,然后遍历输出每个匹配到的连续相同字符。
需要注意的是,反向引用只能在正则表达式中使用,不能在替换字符串的过程中使用。
5. 小结
推荐一个比较好用的网站,正则表达式的匹配规则有很多,很容易记混,只有多加练习才能烂熟于心,用起来才能得心应手。