正则表达式(Regular Expression,简称 regex 或 regexp)是用于匹配字符串的一种模式表达式,常用于文本搜索、替换、提取等任务。下面介绍正则表达式的常用方法及其典型应用。
一、正则表达式字符
正则表达式中的字符主要分为 普通字符 和 特殊字符(元字符)。下面将对这两类字符分别进行详细解释。
1. 普通字符
普通字符就是那些在正则表达式中没有特殊意义的字符,它们表示字面上的含义,直接匹配字符串中的这些字符本身。
常见的普通字符:
- 字母:如
a、b、c等,直接匹配对应的字母。 - 数字:如
0、1、2等,直接匹配对应的数字。 - 标点符号:如
,、!、@等,如果它们没有在元字符列表中,直接匹配这些符号。 - 其他字符:例如空格、下划线
_、加号+等,通常也被视为普通字符,除非它们出现在特定的上下文中。
2. 特殊字符(元字符)
元字符在正则表达式中具有特殊的含义,控制匹配行为。要匹配元字符的字面含义,需要对其进行转义,即在其前面加上反斜杠 \。
常见的元字符及其含义:
| 元字符 | 含义 | 举例 |
|---|---|---|
. | 匹配除换行符外的任何单个字符 | a.b 可以匹配 aab、acb、a1b 等,但不能匹配 ab |
^ | 匹配字符串的开头 | ^abc 只匹配以 abc 开头的字符串,如 abc123 |
$ | 匹配字符串的结尾 | xyz$ 只匹配以 xyz 结尾的字符串,如 123xyz |
* | 匹配前一个字符 0 次或多次(贪婪匹配) | ab*c 可以匹配 ac、abc、abbc、abbbc 等 |
+ | 匹配前一个字符 1 次或多次(贪婪匹配) | ab+c 可以匹配 abc、abbc、abbbc,但不匹配 ac(因为至少需要一个 b) |
? | 匹配前一个字符 0 次或 1 次(可选项)或使量词变为非贪婪模式 | colou?r 可以匹配 color 和 colour |
{} | 限定前一个字符出现的次数 {m} 或出现的范围 {m,n} | a{2,4} 匹配2到4个连续的 a,如 aa、aaa、aaaa |
[] | 字符类,匹配方括号中的任意一个字符 | [abc] 匹配 a、b 或 c |
| ` | ` | 或运算符,匹配其左右两边的任意一个表达式 |
() | 捕获组,分组子表达式,并捕获匹配到的文本 | (abc)+ 匹配 abc、abcabc 等 |
\ | 转义符,用于转义元字符或表示特殊序列(如 \d 表示数字) | . 可以匹配点号 .,因为 . 本身是元字符,转义后才能表示真正的点号 |
常见的转义字符和特殊序列:
| 转义字符/特殊序列 | 含义 |
|---|---|
\d | 匹配任何数字字符(等同于 [0-9]) |
\D | 匹配任何非数字字符(等同于 [^0-9]) |
\w | 匹配任何字母、数字或下划线(等同于 [A-Za-z0-9_]) |
\W | 匹配任何非字母、数字或下划线字符(等同于 [^A-Za-z0-9_]) |
\s | 匹配任何空白字符(空格、制表符、换行符等) |
\S | 匹配任何非空白字符 |
\b | 匹配单词边界 |
\B | 匹配非单词边界 |
\t | 匹配制表符 |
\n | 匹配换行符 |
\r | 匹配回车符 |
\f | 匹配换页符 |
3. 字符类
字符类用方括号 [] 表示,表示匹配括号内任意一个字符。可以使用连字符 - 表示字符范围。
示例:
[abc]:匹配a、b或c。[a-z]:匹配所有小写字母。[A-Z]:匹配所有大写字母。[0-9]:匹配所有数字。[^abc]:匹配除a、b、c以外的任意字符(注意方括号中的^表示取反)。
4. 量词
量词用于指定前一个字符或子表达式的重复次数。
| 量词 | 含义 |
|---|---|
* | 匹配前面的字符 0 次或多次 |
+ | 匹配前面的字符 1 次或多次 |
? | 匹配前面的字符 0 次或 1 次 |
{n} | 匹配前面的字符恰好 n 次 |
{n,} | 匹配前面的字符至少 n 次 |
{n,m} | 匹配前面的字符至少 n 次,最多 m 次 |
5. 锚点
锚点用于匹配文本中的特定位置,而不是字符。
^:匹配字符串的开头。$:匹配字符串的结尾。\b:匹配单词的边界。\B:匹配非单词边界。
二、正则表达式用法
1. 基本用法:匹配与查找
在编程语言中使用正则表达式时,通常会使用一些专门的函数或方法来处理正则表达式。以 Python 中的 re 模块为例:
基本函数:
re.match():从字符串的开头开始匹配,若匹配成功,返回Match对象;否则返回None。re.search():在整个字符串中搜索,返回第一次成功匹配的Match对象;否则返回None。re.findall():返回所有匹配的子串,以列表形式输出。re.finditer():返回所有匹配子串的迭代器,每个匹配都包含Match对象。re.sub():用于替换匹配到的字符串。re.split():按正则表达式分割字符串。
示例:
import re# 示例字符串
text = "There are 3 apples and 5 oranges."
pattern = r"\d+"# 1. re.match() 示例:匹配字符串开头
result = re.match(r"There", text)
print(result.group()) # 输出 'There'# 2. re.search() 示例:查找字符串中的第一个数字
result = re.search(pattern, text)
print(result.group()) # 输出 '3'# 3. re.findall() 示例:查找所有数字
phones = re.findall(pattern, text)
print(phones) # 输出 ['3', '5']# 4. re.sub() 示例:替换数字为 [NUM]
redacted_text = re.sub(pattern, "[NUM]", text)
print(redacted_text) # 输出 'There are [NUM] apples and [NUM] oranges.'# 5. re.split() 示例:按空白字符分割字符串
result = re.split(r"\s+", text)
print(result) # 输出 ['There', 'are', '3', 'apples', 'and', '5', 'oranges.']
三、Match对象
Match 对象是 Python 正则表达式库 re 中返回的一个结果对象,当使用 re.match()、re.search() 或 re.finditer() 成功匹配字符串时,返回的就是这个对象。Match 对象包含了有关匹配结果的各种信息,并提供了一些方法和属性来访问匹配的内容和位置。
Match 对象的常用方法和属性
1. group():获取匹配的内容
group(0)或group():返回整个匹配的字符串。group(n):返回第n个捕获组的匹配内容。使用括号()创建捕获组,group(1)返回第一个捕获组,group(2)返回第二个捕获组,以此类推。
import retext = "My phone number is 123-456-7890."
pattern = r'(\d{3})-(\d{3})-(\d{4})'match = re.search(pattern, text)if match:print(match.group(0)) # 输出 '123-456-7890',整个匹配的字符串print(match.group(1)) # 输出 '123',第一个捕获组print(match.group(2)) # 输出 '456',第二个捕获组print(match.group(3)) # 输出 '7890',第三个捕获组
2. groups():获取所有捕获组的元组
- 返回一个包含所有捕获组的元组(不包含
group(0),即整个匹配)。
import retext = "Date: 2024-09-18"
pattern = r"(\d{4})-(\d{2})-(\d{2})"match = re.search(pattern, text)
if match:print(match.groups()) # 输出 ('2024', '09', '18')
3. 命名捕获组
使用命名捕获组可以给捕获的内容命名,便于后续引用。
import retext = "Order number: 12345, Date: 2024-01-01"
pattern = r"Order number: (?P<order>\d+), Date: (?P<date>\d{4}-\d{2}-\d{2})"
match = re.search(pattern, text)
print(match.group("order")) # 输出 '12345'
print(match.group("date")) # 输出 '2024-01-01'
具体解释:
-
Order number::匹配字符串的开头部分,要求它包含Order number:这个固定的文本。 -
(?P<order>\d+):这是一个命名捕获组,捕获订单号。具体来说:(?P<order>...):捕获组,并命名为order,捕获的内容可以通过group("order")访问。\d+:匹配一个或多个数字字符,即订单号部分(12345)。
-
, Date::匹配订单号后的固定文本, Date:。 -
(?P<date>\d{4}-\d{2}-\d{2}):这是另一个命名捕获组,捕获日期。具体来说:(?P<date>...):捕获组,并命名为date,捕获的内容可以通过group("date")访问。\d{4}:匹配四位数字(年份部分,2024)。-:匹配一个-符号(日期的分隔符)。\d{2}:匹配两位数字(月份和日期部分,01和01)。
整个正则表达式模式匹配的字符串结构是:
Order number: 12345, Date: 2024-01-01
4. groupdict():返回命名捕获组的字典
- 如果正则表达式中使用了命名捕获组(通过
(?P<name>...)的语法),groupdict()会返回一个包含捕获组名称和匹配内容的字典。
import retext = "Order number: 12345, Date: 2024-09-18"
pattern = r"Order number: (?P<order>\d+), Date: (?P<date>\d{4}-\d{2}-\d{2})"match = re.search(pattern, text)
if match:print(match.groupdict()) # 输出 {'order': '12345', 'date': '2024-09-18'}
5. start() 和 end():匹配位置
start():返回匹配到的字符串在原始字符串中的起始位置。end():返回匹配到的字符串在原始字符串中的结束位置。
import retext = "My phone number is 123-456-7890."
pattern = r'\d{3}-\d{3}-\d{4}'match = re.search(pattern, text)if match:print(match.start()) # 输出 19,匹配到的字符串开始的位置print(match.end()) # 输出 31,匹配到的字符串结束的位置
6. span():返回匹配的起始和结束位置
- 返回一个元组,包含匹配到的字符串的起始位置和结束位置。
import retext = "My phone number is 123-456-7890."
pattern = r'\d{3}-\d{3}-\d{4}'match = re.search(pattern, text)if match:print(match.span()) # 输出 (19, 31),匹配到的字符串在原始字符串中的起止位置
7. pos 和 endpos:匹配范围的起始和结束位置
pos:匹配的开始位置(搜索时传递给函数的起始位置)。endpos:匹配的结束位置(搜索时传递给函数的结束位置)。
import retext = "My phone number is 123-456-7890."
pattern = r'\d{3}-\d{3}-\d{4}'match = re.search(pattern, text)if match:print(match.pos) # 输出 0,匹配搜索的起始位置print(match.endpos) # 输出 31,匹配搜索的结束位置
8. re:返回使用的正则表达式
re属性返回编译的正则表达式对象。
import repattern = r'\d{3}-\d{3}-\d{4}'
match = re.search(pattern, "My phone number is 123-456-7890.")if match:print(match.re) # 输出 re.compile('\\d{3}-\\d{3}-\\d{4}')
9. string:返回匹配的字符串
string属性返回进行匹配的原始字符串。
import repattern = r'\d{3}-\d{3}-\d{4}'
match = re.search(pattern, "My phone number is 123-456-7890.")if match:print(match.string) # 输出 'My phone number is 123-456-7890.'
四、re.compile()
在 Python 中,re.compile() 是用于编译正则表达式的函数。它将一个正则表达式字符串编译成一个正则表达式对象,这个对象可以用于多次执行匹配操作,提升性能并简化代码结构。
使用 re.compile() 的好处:
- 提高性能:当你在代码中需要多次使用同一个正则表达式时,使用
re.compile()预先编译正则表达式可以提高效率,避免重复解析正则表达式。 - 代码结构清晰:将正则表达式编译成对象后,能够使代码逻辑更清晰,特别是需要多次使用相同正则表达式时。
语法:
pattern = re.compile(r'正则表达式', flags)
pattern:表示编译后的正则表达式对象。r'正则表达式':待编译的正则表达式字符串,通常使用原始字符串(r'')来防止转义字符的问题。flags:可选参数,用于指定匹配行为的标志(如忽略大小写、支持多行等)。常见标志包括:re.IGNORECASE(re.I):忽略大小写匹配。re.MULTILINE(re.M):多行模式,让^和$匹配每一行的开头和结尾。re.DOTALL(re.S):使.匹配所有字符,包括换行符。
示例:
1. 使用 re.compile() 编译正则表达式:
import re# 编译一个匹配电子邮件的正则表达式
email_pattern = re.compile(r'[\w\.-]+@[\w\.-]+\.\w+')# 使用编译后的模式多次匹配不同的字符串
email1 = "user1@example.com"
email2 = "user2@domain.org"# 使用 compiled 正则表达式对象的 match() 方法
print(email_pattern.match(email1)) # 输出 <re.Match object; span=(0, 17), match='user1@example.com'>
print(email_pattern.match(email2)) # 输出 <re.Match object; span=(0, 16), match='user2@domain.org'>
2. 编译后进行多次操作:
import re# 编译一个匹配数字的正则表达式
number_pattern = re.compile(r'\d+')text = "There are 123 apples and 456 oranges."# 使用 compiled 正则表达式对象的 findall() 方法
numbers = number_pattern.findall(text)
print(numbers) # 输出 ['123', '456']
3. 使用标志参数:
import re# 编译一个忽略大小写的正则表达式
pattern = re.compile(r'hello', re.IGNORECASE)text1 = "Hello World"
text2 = "hello world"# 匹配忽略大小写的字符串
print(pattern.search(text1)) # 输出 <re.Match object; span=(0, 5), match='Hello'>
print(pattern.search(text2)) # 输出 <re.Match object; span=(0, 5), match='hello'>
五、非贪婪匹配
默认情况下,正则表达式是贪婪匹配的,即尽可能多地匹配字符。可以通过在量词后面加 ? 来进行非贪婪匹配。
text = "<html><title>My Page</title></html>"
greedy_pattern = r"<.*>" # 贪婪匹配
non_greedy_pattern = r"<.*?>" # 非贪婪匹配# 贪婪匹配:会匹配整个字符串,因为它尝试匹配尽可能多的字符
print(re.search(greedy_pattern, text).group()) # 输出 '<html><title>My Page</title></html>'# 非贪婪匹配:会匹配到第一个闭合标签
print(re.search(non_greedy_pattern, text).group()) # 输出 '<html>'
