正则表达式(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()
:按正则表达式分割字符串。
示例:
python
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)
返回第二个捕获组,以此类推。
python
import re
text = "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)
,即整个匹配)。
python
import re
text = "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. 命名捕获组
使用命名捕获组可以给捕获的内容命名,便于后续引用。
python
import re
text = "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()
会返回一个包含捕获组名称和匹配内容的字典。
python
import re
text = "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()
:返回匹配到的字符串在原始字符串中的结束位置。
python
import re
text = "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()
:返回匹配的起始和结束位置
- 返回一个元组,包含匹配到的字符串的起始位置和结束位置。
python
import re
text = "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
:匹配的结束位置(搜索时传递给函数的结束位置)。
python
import re
text = "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
属性返回编译的正则表达式对象。
python
import re
pattern = 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
属性返回进行匹配的原始字符串。
python
import re
pattern = 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()
预先编译正则表达式可以提高效率,避免重复解析正则表达式。 - 代码结构清晰:将正则表达式编译成对象后,能够使代码逻辑更清晰,特别是需要多次使用相同正则表达式时。
语法:
python
pattern = re.compile(r'正则表达式', flags)
pattern
:表示编译后的正则表达式对象。r'正则表达式'
:待编译的正则表达式字符串,通常使用原始字符串(r''
)来防止转义字符的问题。flags
:可选参数,用于指定匹配行为的标志(如忽略大小写、支持多行等)。常见标志包括:re.IGNORECASE
(re.I
):忽略大小写匹配。re.MULTILINE
(re.M
):多行模式,让^
和$
匹配每一行的开头和结尾。re.DOTALL
(re.S
):使.
匹配所有字符,包括换行符。
示例:
1. 使用 re.compile()
编译正则表达式:
python
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. 编译后进行多次操作:
python
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. 使用标志参数:
python
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'>
五、非贪婪匹配
默认情况下,正则表达式是贪婪匹配 的,即尽可能多地匹配字符。可以通过在量词后面加 ?
来进行非贪婪匹配。
python
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>'