🇯🇵 日本語 | 🇺🇸 English | 🇪🇸 Español | 🇵🇹 Português | 🇹🇭 ไทย | 🇨🇳 中文

使用Python的re模块掌握正则表达式!字符串搜索与替换完全指南

要在电脑上通过命令提示符或 PowerShell 运行 Python,需要先下载并安装 Python。
如果你还没有安装,请参考Python安装与开发环境配置一文来完成安装。

在制作网站或处理数据时,你是否经常遇到这样的情况:“想从这段文本中只提取特定模式的字符串!”“想批量整理格式混乱的数据!”?在这种时候,能发挥巨大作用的就是“正则表达式”。听起来很难吗?不不不,只要使用Python的`re`模块,即使是初学者也能惊人地轻松成为字符串操作的高手!

本文将通过大量可直接复制粘贴的代码示例,深入浅出地讲解如何使用Python的`re`模块进行正则表达式的基础到应用操作。希望你首先能通过运行代码,体验到“原来是这样用的啊!”的“可运行”的乐趣。来,让我们一起进入正则表达式的世界吧!


正则表达式和re模块到底是什么?

正则表达式,简单来说,就是“用于描述字符串模式的特殊字符串”。例如,它可以像一种通用语言,用于根据模糊条件(如“三个连续的数字”或“电子邮件地址格式”)来搜索和替换字符串。

而在Python中,用于处理正则表达式的标准功能(库)就是`re`模块。只需导入`re`模块,仅用几行代码就能实现复杂的字符串处理。


从基础开始!用于“查找”字符串的4个函数

`re`模块提供了几个用于搜索字符串的函数,我们先来掌握最常用的四个。这些函数都接受“正则表达式模式”和“要搜索的字符串”作为参数。

1. `re.search()` - 返回第一个找到的匹配项

`re.search()`会扫描整个字符串,并返回与模式首次匹配部分的信息。如果未找到,则返回`None`。这是最常用且最易于使用的搜索函数。

例如,我们来试着从一句话中找出“Python”这个词。

<?php
import re

text = "Hello, this is a pen. I love Python programming!"
pattern = "Python"

# 在字符串中搜索模式
match = re.search(pattern, text)

if match:
    print(f"找到了!: '{match.group()}'")
    print(f"起始位置: {match.start()}, 结束位置: {match.end()}")
else:
    print("未找到。")

# 执行结果:
# 找到了!: 'Python'
# 起始位置: 31, 结束位置: 37
?>

2. `re.match()` - 检查字符串“开头”是否匹配

`re.match()`与`re.search()`相似,但有一个主要区别:它只检查模式是否从字符串的开头开始匹配。位于字符串中间的模式将被忽略。

让我们用`re.match()`来处理刚才的句子。它会匹配“Hello”,但不会匹配“Python”。

<?php
import re

text = "Hello, this is a pen. I love Python programming!"

# 模式1: "Hello" (位于字符串开头)
match1 = re.match("Hello", text)
if match1:
    print(f"'Hello'的搜索结果: 匹配成功! -> {match1.group()}")
else:
    print("'Hello'的搜索结果: 匹配失败。")


# 模式2: "Python" (位于字符串中间)
match2 = re.match("Python", text)
if match2:
    print(f"'Python'的搜索结果: 匹配成功! -> {match2.group()}")
else:
    print("'Python'的搜索结果: 匹配失败。")

# 执行结果:
# 'Hello'的搜索结果: 匹配成功! -> Hello
# 'Python'的搜索结果: 匹配失败。
?>

3. `re.findall()` - 以列表形式返回“所有”匹配项

`re.findall()`会找出所有匹配模式的部分,并以字符串列表的形式返回。当你想从一段文本中提取所有数字时,这个函数非常方便。

在这里,我们使用正则表达式模式`\d+`,它表示“一个或多个连续的数字”。

<?php
import re

text = "商品A单价100元,商品B三个250元。订单号是8801。"
# \d+ 是指“一个或多个连续数字”的正则表达式
pattern = r"\d+"

# 获取所有匹配部分并存入列表
numbers = re.findall(pattern, text)

print(f"找到的数字列表: {numbers}")

# 执行结果:
# 找到的数字列表: ['1', '100', '3', '250', '8801']
?>

※ 正则表达式模式前的 `r` 表示“原始字符串”(raw string),它可以防止反斜杠 `\` 被当作转义字符处理。建议在编写正则表达式时总是加上它,以避免不必要的错误。


4. `re.finditer()` - 以“迭代器”形式返回所有匹配项

`re.finditer()`与`re.findall()`相似,但区别在于它返回的是一个“迭代器”而非列表。迭代器适用于需要逐个处理元素的情况,例如在for循环中。

当你不仅需要匹配的字符串,还需要像`search`或`match`返回的那种匹配对象(包含起始位置等信息)时,这个函数就非常有用。

<?php
import re

text = "我的生日是1995年12月31日,他的生日是2003年5月5日。"
# 查找4位数字
pattern = r"\d{4}"

# 以迭代器形式获取匹配项
matches = re.finditer(pattern, text)

print("找到的公元年份:")
for m in matches:
    print(f"- {m.group()} (位置: {m.start()}-{m.end()})")

# 执行结果:
# 找到的公元年份:
# - 1995 (位置: 5-9)
# - 2003 (位置: 21-25)
?>

随心所欲地“替换”字符串 - re.sub()

正则表达式的另一个强大功能是“替换”。使用`re.sub()`,你可以将匹配模式的部分批量替换为另一个字符串。

例如,为了保护隐私,让我们把文本中的电话号码替换为字符串“(不公开)”。我们将使用匹配日本电话号码(如 080-1234-5678)的正则表达式`\d{2,4}-\d{2,4}-\d{4}`。

<?php
import re

text = "如有疑问,请联系支持担当佐藤先生(080-1111-2222),或联系营业担当铃木先生(03-3333-4444)。"
# 匹配电话号码的正则表达式
pattern = r"\d{2,4}-\d{2,4}-\d{4}"
replacement = "(不公开)"

# 替换匹配模式的部分
new_text = re.sub(pattern, replacement, text)

print(new_text)

# 执行结果:
# 如有疑问,请联系支持担当佐藤先生((不公开)),或联系营业担当铃木先生((不公开))。
?>

进阶篇:使用分组进行替换

`re.sub()`的真正威力在于与正则表达式的“分组”功能结合使用。通过用`()`包围模式的一部分,你就可以在替换字符串中重用那部分(即分组)。

例如,让我们把“姓-名”顺序的名字,调换成“名 姓”的顺序。

<?php
import re

text = "登场人物: Tanjiro-Kamado, Zenitsu-Agatsuma, Inosuke-Hashibira"
# (\w+) 是一个分组。第一个匹配姓,第二个匹配名。
pattern = r"(\w+)-(\w+)"
# \2 引用第二个分组(名),\1 引用第一个分组(姓)。
replacement = r"\2 \1"

new_text = re.sub(pattern, replacement, text)
print(new_text)

# 执行结果:
# 登场人物: Kamado Tanjiro, Agatsuma Zenitsu, Hashibira Inosuke
?>

【复制代码就能跑】一起来制作一个正则表达式检查器吧!

利用目前学到的知识,我们来制作一个可以在浏览器上实时确认正则表达式行为的“正则表达式检查器”吧!

请完整复制下面的HTML代码,保存为一个名为`checker.html`的文件,然后在浏览器中打开它。在文本区域输入文本,在正则表达式输入框中输入想尝试的模式(例如 `\d+` 或 `[A-Za-z]+`),然后点击“执行高亮”按钮,匹配的部分就会以浅蓝色高亮显示。这是一个能让你体验“代码跑起来”的绝佳示例!

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>正则表达式高亮检查器</title>
    <style>
        body {
            background-color: #202124;
            color: #e8eaed;
            font-family: sans-serif;
            line-height: 1.6;
            padding: 20px;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
        }
        h1 {
            color: #669df6;
            border-bottom: 1px solid #5f6368;
            padding-bottom: 10px;
        }
        textarea, input[type="text"] {
            width: 100%;
            padding: 10px;
            margin-bottom: 10px;
            background-color: #3c4043;
            color: #e8eaed;
            border: 1px solid #5f6368;
            border-radius: 4px;
            box-sizing: border-box; /* 使宽度计算包含内边距 */
        }
        button {
            padding: 10px 20px;
            background-color: #8ab4f8;
            color: #202124;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
        }
        button:hover {
            opacity: 0.9;
        }
        #result {
            margin-top: 20px;
            padding: 15px;
            border: 1px solid #5f6368;
            border-radius: 4px;
            white-space: pre-wrap; /* 按原样显示换行 */
            word-wrap: break-word; /* 长单词换行 */
        }
        .highlight {
            background-color: #3367d6; /* 用亮蓝色高亮 */
            color: #ffffff;
            border-radius: 3px;
            padding: 0 2px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>正则表达式高亮检查器</h1>
        
        <label for="text-input">要测试的字符串:</label>
        <textarea id="text-input" rows="8">Python 3.10 is the latest version. My email is sample-user@example.com. Please call me at 090-1234-5678. The event is on 2025/07/26.</textarea>

        <label for="regex-input">正则表达式模式:</label>
        <input type="text" id="regex-input" value="\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b">

        <button onclick="highlightMatches()">执行高亮</button>

        <label for="result" style="margin-top: 20px;">结果:</label>
        <div id="result"></div>
    </div>

    <script>
        function highlightMatches() {
            const text = document.getElementById('text-input').value;
            const regexPattern = document.getElementById('regex-input').value;
            const resultDiv = document.getElementById('result');

            if (!text || !regexPattern) {
                resultDiv.textContent = '请输入字符串和正则表达式模式。';
                return;
            }

            try {
                // 添加g标志(全局搜索)和i标志(不区分大小写)
                const regex = new RegExp(regexPattern, 'gi');
                const highlightedText = text.replace(regex, (match) => {
                    return `<span class="highlight">${match}</span>`;
                });
                resultDiv.innerHTML = highlightedText;
            } catch (e) {
                resultDiv.textContent = '无效的正则表达式模式: ' + e.message;
            }
        }
        // 初始加载时也执行
        document.addEventListener('DOMContentLoaded', highlightMatches);
    </script>
</body>
</html>

锦上添花的技巧与注意事项

最后,我们来讲解一些能让你更好地运用正则表达式的技巧,以及初学者容易掉入的陷阱。

贪婪匹配(Greedy)与非贪婪匹配(Non-Greedy)

正则表达式中的 `*` 和 `+` 默认是“贪婪的”(Greedy)。这意味着它们会尝试匹配尽可能长的字符串。

例如,假设你想从字符串 `

contents

` 中只提取 `

` 和 `

` 之间的内容。如果使用模式 `<.*>` 会发生什么呢?

<?php
import re

html = "<p>first paragraph</p><p>second paragraph</p>"

# 贪婪匹配 (Greedy)
greedy_match = re.search(r"<.*>", html)
print(f"贪婪匹配: {greedy_match.group()}")

# 非贪婪匹配 (Non-Greedy): 在 * 后面加上 ?
non_greedy_match = re.search(r"<.*?>", html)
print(f"非贪婪匹配: {non_greedy_match.group()}")

# 执行结果:
# 贪婪匹配: <p>first paragraph</p><p>second paragraph</p>
# 非贪婪匹配: <p>
?>

在贪婪匹配中,它从第一个 `<` 一直匹配到了最后一个 `>`,获取了尽可能长的字符串。而 `*?`(在 `*` 后面加 `?`)则是“非贪婪的”(Non-Greedy),即尝试匹配尽可能短的字符串。这使得它在遇到第一个 `>` 时就停止了匹配。在解析HTML或XML时,这个区别非常重要。


使用 `re.compile()` 提升性能

如果你在程序中需要反复使用同一个正则表达式模式,建议使用 `re.compile()` 事先“编译”该模式。

这样做可以省去Python每次解析模式的开销,从而提高处理速度。编译后的模式对象拥有 `search()`、`findall()` 等方法。

<?php
import re

# 编译电子邮件地址的模式
email_pattern = re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}")

# 使用编译后的对象进行搜索
text1 = "联系方式是 a@b.com。"
match1 = email_pattern.search(text1)
if match1:
    print(f"从Text1中发现: {match1.group()}")

text2 = "支持窗口是 support@example.co.jp。"
match2 = email_pattern.search(text2)
if match2:
    print(f"从Text2中发现: {match2.group()}")

# 执行结果:
# 从Text1中发现: a@b.com
# 从Text2中发现: support@example.co.jp
?>

这样做还有一个好处,就是能让代码更有条理,更清晰易读。


总结

本次我们介绍了使用Python `re`模块的正则表达式基础。刚开始可能会觉得有点难以上手,但一旦你了解了它的强大之处,你就会发现再也离不开它来进行文本处理了。

通过组合这些基础知识并熟练运用元字符,你的数据处理能力将得到飞跃性的提升。请务必复制代码,尝试各种不同的模式,与正则表达式交个朋友吧!

下一步

既然已经能用正则表达式自如地处理文本数据了,接下来不妨挑战一下文件操作吧?特别是CSV文件,它是许多Web应用和数据分析中使用的重要格式。通过下面的文章,来掌握如何使用Python的`csv`模块读写CSV文件吧。

→ 使用csv模块读写CSV文件的方法