Python - 正则表达式

你好,未来的Python巫师们!今天,我们将开始一段激动人心的旅程,进入Python中的正则表达式(regex)的世界。如果你以前从未听说过正则表达式,请不要担心 - 在本教程结束时,你将能够像专业人士一样熟练地使用这个强大的工具!

Python - Reg Expressions

什么是正则表达式?

在我们深入探讨之前,让我们先了解什么是正则表达式。想象一下,你是一名侦探,试图在一片文本的海洋中找到特定的模式。正则表达式就像你的放大镜,帮助你根据模式搜索和操作字符串。很酷,对吧?

原始字符串

在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