Python - 正则表达式
你好,未来的Python巫师们!今天,我们将开始一段激动人心的旅程,进入Python中的正则表达式(regex)的世界。如果你以前从未听说过正则表达式,请不要担心 - 在本教程结束时,你将能够像专业人士一样熟练地使用这个强大的工具!
什么是正则表达式?
在我们深入探讨之前,让我们先了解什么是正则表达式。想象一下,你是一名侦探,试图在一片文本的海洋中找到特定的模式。正则表达式就像你的放大镜,帮助你根据模式搜索和操作字符串。很酷,对吧?
原始字符串
在Python中处理正则表达式时,我们经常使用原始字符串。这些字符串以'r'为前缀,并将反斜杠视为普通字符。这在正则表达式中特别有用,因为反斜杠很常见。
# 普通字符串
print("Hello\nWorld")
# 原始字符串
print(r"Hello\nWorld")
在第一个案例中,你会看到"Hello"和"World"分两行显示。在第二个案例中,你会看到"Hello\nWorld"原样输出。这在处理正则表达式模式时至关重要。
元字符
元字符是正则表达式的构建块。它们具有特殊含义,并帮助我们定义模式。让我们看看一些常见的元字符:
元字符 | 含义 |
---|---|
. | 匹配除换行符之外的任何字符 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
* | 匹配0个或多个重复项 |
+ | 匹配1个或多个重复项 |
? | 匹配0个或1个重复项 |
{} | 匹配明确指定的重复次数 |
[] | 指定要匹配的一组字符 |
\ | 转义特殊字符 |
re.match() 函数
re.match()
函数尝试在字符串开始处匹配一个模式。如果找到匹配,它返回一个匹配对象;否则,返回None。
import re
result = re.match(r"Hello", "Hello, World!")
if result:
print("找到匹配:", result.group())
else:
print("没有匹配")
这将打印"找到匹配: Hello"。group()
方法返回匹配的子字符串。
re.search() 函数
而re.match()
只在字符串开始处寻找匹配,re.search()
会扫描整个字符串寻找匹配。
import re
result = re.search(r"World", "Hello, World!")
if result:
print("找到匹配:", result.group())
else:
print("没有匹配")
这将打印"找到匹配: World"。
匹配与搜索
match()
和search()
之间的主要区别在于,match()
仅在字符串开始处检查匹配,而search()
在字符串的任何位置检查匹配。
re.findall() 函数
re.findall()
函数返回字符串中所有非重叠匹配的模式列表。
import re
text = "The rain in Spain falls mainly in the plain"
result = re.findall(r"ain", text)
print(result)
这将打印['ain', 'ain', 'ain']
。
re.sub() 函数
re.sub()
函数将字符串中所有模式的出现替换为替换字符串。
import re
text = "The rain in Spain"
result = re.sub(r"a", "o", text)
print(result)
这将打印"The roin in Spoin"。
re.compile() 函数
re.compile()
函数创建一个可重用的正则表达式对象,如果你多次使用相同的模式,这可能会更有效。
import re
pattern = re.compile(r"\d+")
result1 = pattern.findall("There are 123 apples and 456 oranges")
result2 = pattern.findall("I have 789 bananas")
print(result1)
print(result2)
这将打印['123', '456']
和['789']
。
re.finditer() 函数
re.finditer()
函数返回一个迭代器,为字符串中所有非重叠匹配的模式生成匹配对象。
import re
text = "The rain in Spain"
for match in re.finditer(r"ain", text):
print(f"找到 '{match.group()}' 在位置 {match.start()}-{match.end()}")
这将打印:
找到 'ain' 在位置 5-8
找到 'ain' 在位置 17-20
Python正则表达式的用例
正则表达式有许多实际应用。让我们看一个常见的用例:
找出以元音字母开头的单词
import re
text = "An apple a day keeps the doctor away"
vowel_words = re.findall(r'\b[aeiouAEIOU]\w+', text)
print(vowel_words)
这将打印['An', 'apple', 'a', 'away']
。
正则表达式修饰符:选项标志
Python的re模块提供了几个选项标志,这些标志修改模式的解释方式:
标志 | 描述 |
---|---|
re.IGNORECASE (re.I) | 执行不区分大小写的匹配 |
re.MULTILINE (re.M) | 使^匹配每一行的开始,$匹配每一行的结束 |
re.DOTALL (re.S) | 使.匹配任何字符,包括换行符 |
re.VERBOSE (re.X) | 允许你编写更可读的正则表达式模式 |
正则表达式模式
让我们探索一些更高级的模式:
字符类
字符类允许你指定要匹配的一组字符:
import re
text = "The quick brown fox jumps over the lazy dog"
result = re.findall(r"[aeiou]", text)
print(result)
这将打印文本中找到的所有元音。
特殊字符类
Python正则表达式支持特殊字符类:
类 | 描述 |
---|---|
\d | 匹配任何十进制数字 |
\D | 匹配任何非数字字符 |
\s | 匹配任何空白字符 |
\S | 匹配任何非空白字符 |
\w | 匹配任何字母数字字符 |
\W | 匹配任何非字母数字字符 |
重复情况
我们可以指定一个模式应该出现多少次:
import re
text = "I have 111 apples and 22 oranges"
result = re.findall(r"\d{2,3}", text)
print(result)
这将打印['111', '22']
,匹配2或3位数字。
非贪婪重复
默认情况下,重复是贪婪的,意味着它会尽可能多地匹配。在重复后面添加一个?使其变为非贪婪:
import re
text = "<h1>Title</h1><p>Paragraph</p>"
greedy = re.findall(r"<.*>", text)
non_greedy = re.findall(r"<.*?>", text)
print("Greedy:", greedy)
print("Non-greedy:", non_greedy)
这将显示贪婪和非贪婪匹配之间的差异。
使用括号进行分组
括号允许你将正则表达式的部分分组:
import re
text = "John Smith ([email protected])"
result = re.search(r"(\w+) (\w+) \((\w+@\w+\.\w+)\)", text)
if result:
print(f"全名: {result.group(1)} {result.group(2)}")
print(f"电子邮件: {result.group(3)}")
这将从文本中提取名称和电子邮件。
后向引用
后向引用允许你引用之前匹配的组:
import re
text = "<h1>Title</h1><p>Paragraph</p>"
result = re.findall(r"<(\w+)>.*?</\1>", text)
print(result)
这将匹配打开和关闭的HTML标签。
替代项
|字符允许你指定替代项:
import re
text = "The color of the sky is blue or gray"
result = re.search(r"blue|gray", text)
if result:
print(f"找到颜色: {result.group()}")
这将匹配"blue"或"gray"。
锚点
锚点指定文本中的位置:
import re
text = "Python is awesome"
start = re.match(r"^Python", text)
end = re.search(r"awesome$", text)
print(f"以Python开头: {bool(start)}")
print(f"以awesome结尾: {bool(end)}")
这将检查文本是否以"Python"开头并以"awesome"结尾。
括号中的特殊语法
括号不仅仅用于分组:
- (?:...) 创建一个非捕获组
- (?P
...) 创建一个命名组 - (?=...) 创建一个正向先行断言
- (?!...) 创建一个负向先行断言
import re
text = "Python version 3.9.5"
result = re.search(r"Python (?:version )?(?P<version>\d+\.\d+\.\d+)", text)
if result:
print(f"版本: {result.group('version')}")
这将提取版本号,无论"text"中是否存在"version"。
就这样,各位!我们已经穿越了Python正则表达式的领域,从基础到一些相当高级的概念。请记住,像任何强大的工具一样,正则表达式需要练习才能掌握。所以,如果你一开始觉得有些不知所措,请不要气馁。继续实验,很快你就能像一个专业的侦探一样找到模式了!祝编码愉快!
Credits: Image by storyset