Python3正则表达式:字符串魔法师的指南[特殊字符]‍♂️

发布于:2025-05-09 ⋅ 阅读:(20) ⋅ 点赞:(0)

什么是正则表达式?

想象一下,你是一位文本世界的探险家,需要在茫茫字符海洋中寻找特定的"宝藏"(模式)。正则表达式就是你的"寻宝地图"!

正则表达式(Regular Expression,简称regex)是一种强大的字符串搜索、匹配和操作的模式语言。它就像一个超级搜索工具,能帮你:

  • 🔍 查找特定模式的文本
  • ✂️ 替换文本中的特定内容
  • ✅ 验证文本是否符合特定格式(如邮箱、电话号码)
  • 🪓 分割文本为多个部分

在Python中使用正则表达式

Python通过re模块提供正则表达式支持:

import re

一、正则表达式基础语法:你的魔法咒语

基本匹配符

符号 作用 就像在说…
. 匹配任意单个字符(除了换行符) “我不挑食,给我任何一个字符就行!”
^ 匹配字符串开头 “我只关心句子的开头!”
$ 匹配字符串结尾 “我只看结局!”
\ 转义特殊字符 “别用魔法攻击我,这只是个普通字符!”

字符类:性格各异的字符们

符号 作用 就像在说…
[abc] 匹配方括号内的任意一个字符 “给我一个a或b或c就满足了”
[^abc] 匹配除了方括号内的任意字符 “除了abc,我都喜欢!”
[a-z] 匹配a到z范围内的任意小写字母 “给我一个小写字母就行”
[A-Z] 匹配A到Z范围内的任意大写字母 “我只收大写字母!”
[0-9] 匹配任意数字 “数字就行,不挑”

预定义字符类:常见角色的快捷方式

符号 作用 就像在说…
\d 匹配任意数字,等同于[0-9] “来个数字吧!”
\D 匹配任意非数字,等同于[^0-9] “数字以外的都行!”
\w 匹配字母、数字或下划线,等同于[a-zA-Z0-9_] “给我一个’单词’字符!”
\W 匹配非字母、数字、下划线的字符 “不要’单词’字符!”
\s 匹配任意空白字符(空格、制表符、换行符等) “我需要一点空间!”
\S 匹配任意非空白字符 “不要空白!”

重复限定符:贪婪的收集者

符号 作用 就像在说…
* 匹配前面的模式零次或多次 “有多少要多少,没有也行!”
+ 匹配前面的模式一次或多次 “至少给我一个,多了也行!”
? 匹配前面的模式零次或一次 “有一个我就满足了,没有也无所谓”
{n} 精确匹配前面的模式n次 “我要正好n个,不多不少!”
{n,} 匹配前面的模式至少n次 “给我至少n个!”
{n,m} 匹配前面的模式n到m次 “给我n到m个之间,我很好说话的”

贪婪vs非贪婪:胃口大小问题

默认情况下,重复限定符是贪婪的,会尽可能多地匹配:

# 贪婪模式:尽可能多地匹配
re.search(r'a.*b', 'aabab').group()  # 'aabab'

加上?后,变成非贪婪模式,会尽可能少地匹配:

# 非贪婪模式:尽可能少地匹配
re.search(r'a.*?b', 'aabab').group()  # 'aab'

分组与捕获:给模式戴上名牌

符号 作用 就像在说…
(...) 分组并捕获匹配的内容 “这几个字符是一伙的,我要记住他们!”
(?:...) 分组但不捕获匹配的内容 “这几个字符是一伙的,但不用记住他们”
(?P<name>...) 命名捕获组 “这几个字符是一伙的,叫他们’name’”

二、Python中的re模块:施展魔法的工具箱

主要函数

函数 作用 记忆小窍门
re.search(pattern, string) 在字符串中搜索第一个匹配项 “找一找就好”
re.match(pattern, string) 从字符串开头匹配模式 “必须从头开始找”
re.findall(pattern, string) 返回所有匹配项的列表 “全都要找出来!”
re.finditer(pattern, string) 返回所有匹配项的迭代器 “一个一个慢慢找”
re.sub(pattern, repl, string) 替换所有匹配项 “找到就换掉”
re.split(pattern, string) 按匹配项分割字符串 “看到就切一刀”
re.compile(pattern) 编译正则表达式模式 “先准备好魔法卷轴”

常用标志

标志 作用 记忆小窍门
re.IGNORECASEre.I 忽略大小写 “A和a都一样”
re.MULTILINEre.M 多行模式,使^$匹配每行的开始和结束 “每行都很重要”
re.DOTALLre.S 使.也能匹配换行符 “点点无所不包”

三、实际例子:魔法入门

1. 验证邮箱地址

import re

def is_valid_email(email):
    pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
    return bool(re.match(pattern, email))

# 测试一下
print(is_valid_email('user@example.com'))  # True
print(is_valid_email('invalid-email'))     # False

2. 提取文本中的日期

import re

text = "今天是2023-05-15,明天是2023-05-16,后天是2023/05/17"

# 找出所有日期
dates = re.findall(r'\d{4}[-/]\d{2}[-/]\d{2}', text)
print(dates)  # ['2023-05-15', '2023-05-16', '2023/05/17']

3. 提取并处理HTML标签

import re

html = "<div>Hello <b>Python</b> and <i>Regex</i></div>"

# 找出所有标签
tags = re.findall(r'<[^>]+>', html)
print(tags)  # ['<div>', '<b>', '</b>', '<i>', '</i>', '</div>']

# 去除所有标签
clean_text = re.sub(r'<[^>]+>', '', html)
print(clean_text)  # 'Hello Python and Regex'

4. 分割CSV,但忽略引号内的逗号

# 这个例子展示了复杂模式的威力
import re

csv_line = 'John,"Doe,Jr",New York,USA'

# 错误的简单分割
print(csv_line.split(','))  # ['John', '"Doe', 'Jr"', 'New York', 'USA']

# 使用正则表达式正确分割
pattern = r',(?=(?:[^"]*"[^"]*")*[^"]*$)'
print(re.split(pattern, csv_line))  # ['John', '"Doe,Jr"', 'New York', 'USA']

四、正则表达式调试技巧:魔法训练

  1. 小步前进:先测试简单的模式,然后逐渐添加复杂度
  2. 在线工具:使用regex101.com等在线工具进行可视化测试
  3. 分组使用:使用分组来隔离和测试正则表达式的各个部分
  4. 命名捕获组:给重要的捕获组命名,增强可读性

五、正则表达式性能注意事项:魔力消耗控制

  1. 避免过度回溯:复杂的嵌套重复限定符可能导致灾难性回溯
  2. 预编译模式:频繁使用的模式应该预编译
    phone_pattern = re.compile(r'\d{3}-\d{3}-\d{4}')
    # 重复使用phone_pattern.search()而不是re.search()
    
  3. 非捕获组:当不需要捕获结果时,使用非捕获组(?:...)提高性能
  4. 适当使用原子组:减少回溯的可能性

总结:正则表达式修炼指南

  • 正则表达式是强大的文本处理工具,但需要时间掌握
  • 从简单模式开始,逐步构建复杂模式
  • 多练习,多实验,才能掌握这门"魔法"
  • 记住:有时候,简单的字符串方法可能更适合简单的任务

“一开始,所有的正则表达式都像天书;熟练后,它们变成了你的得力助手。” – 正则表达式大师的传说

练习题:小试身手

  1. 编写一个正则表达式来验证中国手机号码(11位数字,以1开头)
  2. 从文本中提取所有的URL链接
  3. 验证一个字符串是否为有效的IPv4地址
  4. 从文本中提取所有的"#标签"(Twitter风格)

1.验证中国手机号码

中国手机号码规则:11位数字,以1开头。

import re

def validate_chinese_phone(phone_number):
    """
    验证中国手机号码是否有效
    
    参数:
        phone_number: 要验证的手机号码字符串
    
    返回:
        布尔值: 是否为有效的中国手机号码
    """
    # 正则表达式解释:
    # ^1           - 以1开头
    # [0-9]{10}    - 后跟10位数字
    # $            - 结束匹配
    pattern = r'^1[0-9]{10}$'
    
    return bool(re.match(pattern, phone_number))

# 测试示例
test_phones = [
    "13812345678",  # 有效
    "19912345678",  # 有效
    "12345678901",  # 有效 (只要求以1开头的11位数字)
    "138123456789", # 无效 (12位)
    "2381234567",   # 无效 (10位且不以1开头)
    "138abcd1234",  # 无效 (包含非数字字符)
    "01381234567",  # 无效 (12位且不以1开头)
    "138-1234-567"  # 无效 (包含连字符)
]

for phone in test_phones:
    result = "有效" if validate_chinese_phone(phone) else "无效"
    print(f"手机号 {phone}{result}的中国手机号")

2. 从文本中提取所有URL链接

提取各种格式的URL链接,包括http、https、ftp等协议。

import re

def extract_urls(text):
    """
    从文本中提取所有URL链接
    
    参数:
        text: 要搜索的文本
        
    返回:
        列表: 包含所有找到的URL
    """
    # 正则表达式解释:
    # (https?|ftp)://                - 匹配协议(http://, https://, ftp://)
    # [-a-zA-Z0-9@:%._\+~#=]{1,256}  - 匹配域名和路径中的有效字符
    # \.[a-zA-Z0-9()]{1,6}           - 匹配顶级域名(.com, .org等)
    # \b                             - 单词边界
    # ([-a-zA-Z0-9()@:%_\+.~#?&//=]*)- 匹配URL中的参数和路径
    pattern = r'(https?|ftp)://[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)'
    
    # 简化版本,也可以使用这个更简单但覆盖面广的模式
    # pattern = r'https?://[^\s]+'
    
    return re.findall(pattern, text)

# 测试示例
test_text = """
这是一个包含多个URL的文本示例:
访问 https://www.example.com 获取更多信息。
下载链接: http://files.example.org/document.pdf
FTP服务器: ftp://ftp.example.net/downloads/
无效链接: www.not-extracted.com (因为没有协议前缀)
嵌入在文本中的链接https://api.example.com/v1/data?id=123&format=json也会被提取。
"""

urls = extract_urls(test_text)
print("提取的URL:")
for i, url in enumerate(urls, 1):
    print(f"{i}. {url[0]}{url[1]}")  # 合并捕获组

3. 验证IPv4地址

验证一个字符串是否为有效的IPv4地址(四个0-255之间的数字,用点分隔)。

import re

def validate_ipv4(ip_address):
    """
    验证一个字符串是否为有效的IPv4地址
    
    参数:
        ip_address: 要验证的IP地址字符串
        
    返回:
        布尔值: 是否为有效的IPv4地址
    """
    # 正则表达式解释:
    # ^                         - 开始匹配
    # (25[0-5]|2[0-4][0-9]|     - 匹配250-255或200-249
    #  [01]?[0-9][0-9]?|        - 匹配0-199
    #  [0-9])                   - 匹配单个数字0-9
    # \.                        - 匹配点号(.)
    # 重复上述模式三次,最后一次不带点号
    # $                         - 结束匹配
    pattern = r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$'
    
    return bool(re.match(pattern, ip_address))

# 测试示例
test_ips = [
    "192.168.1.1",    # 有效
    "10.0.0.1",       # 有效
    "172.16.254.1",   # 有效
    "255.255.255.255",# 有效
    "0.0.0.0",        # 有效
    "256.0.0.1",      # 无效 (256超出范围)
    "192.168.1",      # 无效 (只有3段)
    "192.168.1.1.1",  # 无效 (有5段)
    "192.168.1.a",    # 无效 (包含非数字字符)
    "192.168.01.1"    # 有效 (前导零被允许)
]

for ip in test_ips:
    result = "有效" if validate_ipv4(ip) else "无效"
    print(f"IP地址 {ip}{result}的IPv4地址")

4. 提取Twitter风格的标签

从文本中提取所有的"#标签"(Twitter风格)。

import re

def extract_hashtags(text):
    """
    从文本中提取所有的Twitter风格标签(#标签)
    
    参数:
        text: 要搜索的文本
        
    返回:
        列表: 包含所有找到的标签(不含#符号)
    """
    # 正则表达式解释:
    # #                   - 匹配#符号
    # ([a-zA-Z0-9_\u4e00-\u9fa5]+) - 匹配标签内容:
    #   [a-zA-Z0-9_]     - 英文字母、数字和下划线
    #   \u4e00-\u9fa5    - 中文字符范围
    pattern = r'#([a-zA-Z0-9_\u4e00-\u9fa5]+)'
    
    return re.findall(pattern, text)

# 测试示例
test_text = """
今天的天气真不错 #天气 #晴天
我正在学习Python #Python #编程 #学习
这是一个#复合标签 和一个 #带有标点符号的标签!
#中英文混合tag #123数字 #with_underscore
"""

hashtags = extract_hashtags(test_text)
print("提取的标签:")
for i, tag in enumerate(hashtags, 1):
    print(f"{i}. {tag}")

综合示例应用

下面是一个综合应用,将上面的所有函数整合到一个文本分析工具中:

import re

class TextAnalyzer:
    """文本分析工具,提供多种正则表达式功能"""
    
    @staticmethod
    def validate_chinese_phone(phone_number):
        pattern = r'^1[0-9]{10}$'
        return bool(re.match(pattern, phone_number))
    
    @staticmethod
    def extract_urls(text):
        pattern = r'(https?|ftp)://[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)'
        matches = re.findall(pattern, text)
        return [f"{protocol}{path}" for protocol, path in matches]
    
    @staticmethod
    def validate_ipv4(ip_address):
        pattern = r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$'
        return bool(re.match(pattern, ip_address))
    
    @staticmethod
    def extract_hashtags(text):
        pattern = r'#([a-zA-Z0-9_\u4e00-\u9fa5]+)'
        return re.findall(pattern, text)
    
    @staticmethod
    def analyze_text(text):
        """分析文本,提取所有可能的信息"""
        result = {
            "phones": [],
            "urls": [],
            "ips": [],
            "hashtags": []
        }
        
        # 提取手机号
        phone_pattern = r'1[0-9]{10}'
        phones = re.findall(phone_pattern, text)
        result["phones"] = [p for p in phones if TextAnalyzer.validate_chinese_phone(p)]
        
        # 提取URL
        result["urls"] = TextAnalyzer.extract_urls(text)
        
        # 提取IP地址
        ip_pattern = r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
        potential_ips = re.findall(ip_pattern, text)
        result["ips"] = [ip for ip in potential_ips if TextAnalyzer.validate_ipv4(ip)]
        
        # 提取标签
        result["hashtags"] = TextAnalyzer.extract_hashtags(text)
        
        return result

# 测试综合分析
sample_text = """
联系我: 13812345678 或 19987654321
网站: https://www.example.com/path?query=123
服务器IP: 192.168.1.1 和 8.8.8.8 (但 999.999.999.999 不是有效IP)
话题: #Python #正则表达式 #数据分析
"""

analyzer = TextAnalyzer()
analysis_result = analyzer.analyze_text(sample_text)

print("文本分析结果:")
print(f"手机号: {analysis_result['phones']}")
print(f"URL链接: {analysis_result['urls']}")
print(f"IP地址: {analysis_result['ips']}")
print(f"话题标签: {analysis_result['hashtags']}")

这些正则表达式示例展示了如何使用Python处理各种文本模式匹配任务。您可以根据具体需求进一步调整这些模式,使它们更加精确或更适合您的用例。

记住:正则表达式就像厨艺,多练习才能精通。每解决一个文本处理问题,你就离正则表达式大师更近一步!


网站公告

今日签到

点亮在社区的每一天
去签到