Python 正则表达式教程:帮你轻松实现文本匹配
正则表达式(Regular Expression,简称 Regex 或 RE)是一种强大的文本处理工具,它使用一种特定的语法模式来描述、匹配和操作字符串。无论你是数据分析师、网络工程师、Web 开发人员,还是仅仅想从文本文件中提取特定信息,正则表达式都能成为你的得力助手。Python 通过内置的 re
模块提供了对正则表达式的全面支持。
本教程将带你深入了解 Python 正则表达式的方方面面,从基础概念到高级技巧,通过丰富的示例代码,让你轻松掌握文本匹配的精髓。
1. 正则表达式基础
1.1 什么是正则表达式?
正则表达式本质上是一个由字符和特殊符号组成的字符串,它定义了一个搜索模式。这个模式可以用来:
- 验证字符串:检查字符串是否符合某种格式(例如,电子邮件地址、电话号码)。
- 查找字符串:在文本中找到符合特定模式的所有子字符串。
- 替换字符串:将文本中符合特定模式的子字符串替换为其他内容。
- 分割字符串:根据特定模式将字符串分割成多个部分。
1.2 为什么要使用正则表达式?
想象一下,你需要从一个包含大量电话号码的文本文件中提取所有有效的电话号码。如果不用正则表达式,你可能需要编写复杂的代码,逐个字符地检查,处理各种不同的电话号码格式。而使用正则表达式,你只需要一个简洁的表达式就能完成这个任务。
正则表达式的优势在于:
- 简洁性:用简短的表达式描述复杂的匹配规则。
- 高效性:正则表达式引擎通常经过高度优化,能够快速处理大量文本。
- 通用性:正则表达式在各种编程语言和工具中都得到广泛支持。
1.3 Python re
模块
Python 的 re
模块提供了对正则表达式的全面支持。要使用正则表达式,首先需要导入 re
模块:
python
import re
re
模块提供了许多函数,用于执行各种正则表达式操作。我们将在后续章节中详细介绍这些函数。
2. 正则表达式语法
正则表达式的语法是学习的重点。它由普通字符和特殊字符(元字符)组成。
2.1 普通字符
普通字符是指除了元字符之外的所有字符,包括字母、数字、下划线和大多数其他字符。普通字符在正则表达式中表示它们自身。
例如,正则表达式 python
将匹配字符串 “python”。
2.2 元字符
元字符是具有特殊含义的字符,它们用于构建更复杂的匹配模式。以下是常用的元字符:
元字符 | 描述 |
---|---|
. |
匹配除换行符 (\n ) 之外的任何单个字符。 |
^ |
匹配字符串的开头。 |
$ |
匹配字符串的结尾。 |
* |
匹配前面的字符零次或多次。 |
+ |
匹配前面的字符一次或多次。 |
? |
匹配前面的字符零次或一次。 |
{n} |
匹配前面的字符恰好 n 次。 |
{n,} |
匹配前面的字符至少 n 次。 |
{n,m} |
匹配前面的字符至少 n 次,但不超过 m 次。 |
[] |
匹配方括号内的任何一个字符。例如,[abc] 匹配 “a”、”b” 或 “c”。 |
[^] |
匹配不在方括号内的任何一个字符。例如,[^abc] 匹配除了 “a”、”b” 和 “c” 之外的任何字符。 |
\ |
转义字符,用于取消元字符的特殊含义。例如,\. 匹配实际的点号 (. )。 |
| |
或运算符,匹配 | 两侧的任何一个表达式。例如,a|b 匹配 “a” 或 “b”。 |
() |
分组,将括号内的表达式视为一个整体,并可以捕获匹配的子字符串。 |
2.3 字符类
字符类用于匹配一组字符中的任何一个。
[abc]
:匹配 “a”、”b” 或 “c”。[a-z]
:匹配任何小写字母。[A-Z]
:匹配任何大写字母。[0-9]
:匹配任何数字。[a-zA-Z0-9]
:匹配任何字母或数字。[^abc]
:匹配除了 “a”、”b” 和 “c” 之外的任何字符。
2.4 预定义字符类
为了方便,re
模块提供了一些预定义的字符类:
预定义字符类 | 描述 | 等价的字符类 |
---|---|---|
\d |
匹配任何数字 | [0-9] |
\D |
匹配任何非数字字符 | [^0-9] |
\w |
匹配任何字母、数字或下划线 | [a-zA-Z0-9_] |
\W |
匹配任何非字母、数字或下划线字符 | [^a-zA-Z0-9_] |
\s |
匹配任何空白字符(空格、制表符、换行符等) | [ \t\n\r\f\v] |
\S |
匹配任何非空白字符 | [^ \t\n\r\f\v] |
2.5 量词
量词用于指定前面的字符或分组出现的次数。
*
:匹配零次或多次。+
:匹配一次或多次。?
:匹配零次或一次。{n}
:匹配恰好 n 次。{n,}
:匹配至少 n 次。{n,m}
:匹配至少 n 次,但不超过 m 次。
2.6 贪婪与非贪婪
默认情况下,量词是“贪婪的”,这意味着它们会尽可能多地匹配字符。在量词后面加上 ?
可以使其变为“非贪婪的”,这意味着它们会尽可能少地匹配字符。
例如,对于字符串 “abbbb”,正则表达式 ab+
(贪婪)将匹配 “abbbb”,而 ab+?
(非贪婪)将只匹配 “ab”。
2.7 分组和捕获
圆括号 ()
用于将正则表达式的一部分分组,并可以捕获匹配的子字符串。捕获的子字符串可以通过 group()
方法访问。
例如,正则表达式 (\d{3})-(\d{3})-(\d{4})
将匹配美国电话号码,并将区号、前缀和线路号分别捕获到三个分组中。
2.8 反向引用
反向引用允许你在正则表达式中引用先前捕获的分组。\1
表示第一个分组,\2
表示第二个分组,依此类推。
例如,正则表达式 (\w+)\s+\1
将匹配重复的单词,如 “hello hello”。
2.9 零宽断言
零宽断言是一种特殊的正则表达式结构,它用于匹配位置,而不是字符。
^
:匹配字符串的开头。$
:匹配字符串的结尾。\b
:匹配单词边界(单词字符和非单词字符之间的位置)。\B
:匹配非单词边界。(?=...)
: 正向肯定预查(Positive Lookahead),匹配后面是…的位置(?!...)
: 正向否定预查 (Negative Lookahead), 匹配后面不是…的位置(?<=...)
: 反向肯定预查(Positive Lookbehind),匹配前面是…的位置(?<!...)
: 反向否定预查(Negative Lookbehind),匹配前面不是…的位置
零宽断言不会消耗字符,它们只是检查匹配位置是否符合条件。
例如:
\w+(?=ing)
:匹配所有以”ing”结尾的单词,但不包括 “ing” 本身。
(?<=Mr\.\s)\w+
: 匹配所有在”Mr. “之后的单词,但不包括“Mr. ”。
3. re
模块常用函数
re
模块提供了许多函数来执行正则表达式操作。以下是一些常用的函数:
3.1 re.compile(pattern, flags=0)
将正则表达式编译成一个正则表达式对象,以便重复使用。这可以提高效率,特别是当你在循环中多次使用同一个正则表达式时。
python
pattern = re.compile(r'\d+') # 编译一个匹配数字的正则表达式
result = pattern.findall('There are 123 apples and 456 oranges.')
print(result) # 输出: ['123', '456']
3.2 re.search(pattern, string, flags=0)
在字符串中搜索第一个与正则表达式匹配的子字符串。如果找到匹配项,返回一个匹配对象;否则返回 None
。
python
match = re.search(r'apple', 'I have an apple and a banana.')
if match:
print(match.group()) # 输出: apple
3.3 re.match(pattern, string, flags=0)
尝试从字符串的开头匹配正则表达式。如果匹配成功,返回一个匹配对象;否则返回 None
。
“`python
match = re.match(r’I’, ‘I have an apple.’)
if match:
print(match.group()) # 输出: I
match = re.match(r’apple’, ‘I have an apple.’)
if match:
print(match.group()) # 不会输出,因为 apple 不在字符串开头
“`
3.4 re.findall(pattern, string, flags=0)
在字符串中查找所有与正则表达式匹配的非重叠子字符串,并返回一个列表。
python
result = re.findall(r'\d+', 'There are 123 apples and 456 oranges.')
print(result) # 输出: ['123', '456']
3.5 re.finditer(pattern, string, flags=0)
在字符串中查找所有与正则表达式匹配的非重叠子字符串,并返回一个迭代器,每个元素都是一个匹配对象。
“`python
for match in re.finditer(r’\w+’, ‘apple, banana, orange’):
print(match.group(), match.start(), match.end())
输出:
apple 0 5
banana 7 13
orange 15 21
“`
3.6 re.sub(pattern, repl, string, count=0, flags=0)
将字符串中所有与正则表达式匹配的子字符串替换为 repl
。repl
可以是一个字符串,也可以是一个函数。如果 repl
是一个函数,它将接收一个匹配对象作为参数,并返回用于替换的字符串。count
参数指定最多替换的次数,如果为 0(默认值),则替换所有匹配项。
“`python
result = re.sub(r’\s+’, ‘-‘, ‘This is a string with multiple spaces.’)
print(result) # 输出: This-is-a-string-with-multiple-spaces.
def double_number(match):
value = int(match.group(0))
return str(value * 2)
result = re.sub(r’\d+’, double_number, ‘There are 123 apples.’)
print(result) # 输出: There are 246 apples.
“`
3.7 re.split(pattern, string, maxsplit=0, flags=0)
根据正则表达式的匹配项分割字符串。maxsplit
参数指定最大分割次数,如果为 0(默认值),则分割所有匹配项。
“`python
result = re.split(r’,’, ‘apple,banana,orange’)
print(result) # 输出: [‘apple’, ‘banana’, ‘orange’]
result = re.split(r’\W+’, ‘This is a sentence, with punctuation.’) #以非单词字符分割
print(result) # 输出: [‘This’, ‘is’, ‘a’, ‘sentence’, ‘with’, ‘punctuation’, ”]
“`
3.8 re.escape(pattern)
对字符串中所有可能被解释为正则表达式运算符的字符进行转义。
python
escaped_pattern = re.escape("*.py")
print(escaped_pattern) # 输出: \*\.py
3.9 匹配对象的方法
如果 re.search()
、re.match()
或 re.finditer()
返回一个匹配对象,你可以使用以下方法访问匹配的信息:
group(n=0)
:返回第 n 个分组匹配的子字符串。如果 n 为 0(默认值),返回整个匹配项。groups()
:返回一个包含所有分组匹配的子字符串的元组。start(n=0)
:返回第 n 个分组匹配的子字符串在原始字符串中的起始位置。end(n=0)
:返回第 n 个分组匹配的子字符串在原始字符串中的结束位置。span(n=0)
:返回一个包含第 n 个分组匹配的子字符串的起始和结束位置的元组。
python
match = re.search(r'(\w+)-(\w+)', 'hello-world')
print(match.group()) # 输出: hello-world
print(match.group(1)) # 输出: hello
print(match.group(2)) # 输出: world
print(match.groups()) # 输出: ('hello', 'world')
print(match.start(1)) # 输出: 0
print(match.end(2)) # 输出: 11
print(match.span()) # 输出: (0, 11)
4. 常用正则表达式示例
以下是一些常用的正则表达式示例,可以帮助你更好地理解正则表达式的用法:
4.1 匹配电子邮件地址
python
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
这个正则表达式可以匹配大多数常见的电子邮件地址格式。
4.2 匹配 URL
python
pattern = r'https?://(?:www\.)?[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(?:/[^\s]*)?'
这个正则表达式可以匹配 HTTP 和 HTTPS URL。
4.3 匹配 IP 地址
python
pattern = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
这个正则表达式可以匹配 IPv4 地址。
4.4 匹配日期 (YYYY-MM-DD)
python
pattern = r'^\d{4}-\d{2}-\d{2}$'
这个表达式可以匹配YYYY-MM-DD格式的日期。
4.5 匹配HTML标签
python
pattern = r'<[^>]+>' # 简单版本,可能无法处理所有复杂情况
这个表达式可以匹配HTML标签,如<p>
, <div>
, <span>
等。
4.6 从HTML中提取所有链接
python
pattern = r'<a\s+href="([^"]+)"'
利用分组捕获href
属性的值。
4.7 验证密码强度
python
pattern = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'
这个表达式要求密码至少8位,包含至少一个大写字母,一个小写字母,一个数字和一个特殊字符。
5. 正则表达式标志 (Flags)
re
模块的函数通常接受一个可选的 flags
参数,用于修改正则表达式的行为。以下是一些常用的标志:
re.IGNORECASE
或re.I
:忽略大小写。re.MULTILINE
或re.M
:多行模式,^
和$
匹配每一行的开头和结尾,而不仅仅是整个字符串的开头和结尾。re.DOTALL
或re.S
:点号 (.
) 匹配任何字符,包括换行符。re.VERBOSE
或re.X
: 详细模式。 允许你编写更易读的正则表达式,通过添加空格和注释。re.ASCII
或re.A
: 让\w
,\W
,\b
,\B
,\d
,\D
,\s
和\S
只匹配 ASCII 字符。
“`python
忽略大小写
result = re.findall(r’apple’, ‘Apple and apple’, re.IGNORECASE)
print(result) # 输出: [‘Apple’, ‘apple’]
多行模式
text = “line1\nline2\nline3”
result = re.findall(r’^line’, text, re.MULTILINE)
print(result) # 输出: [‘line’, ‘line’, ‘line’]
使用VERBOSE标志
pattern = re.compile(r”’
\d{3} # 区号
– # 分隔符
\d{3} # 前缀
– # 分隔符
\d{4} # 线路号
”’, re.VERBOSE)
result = pattern.findall(“555-123-4567”)
print(result) # 输出: [‘555-123-4567’]
“`
6. 总结
Python 正则表达式是一个强大的工具,可以帮助你轻松实现文本匹配、查找、替换和分割。通过掌握正则表达式的语法和 re
模块的函数,你可以高效地处理各种文本处理任务。
本教程介绍了正则表达式的基础知识、语法、常用函数和示例。希望通过本教程,你能够掌握 Python 正则表达式,并在实际工作中灵活运用。记住,实践是掌握正则表达式的最佳途径,多写多练,你就能成为正则表达式高手!