Python正则表达式提取技巧:轻松掌握高效方法 – wiki基地

Python 正则表达式提取技巧:轻松掌握高效方法

正则表达式 (Regular Expression,简称 Regex) 是处理字符串的强大工具,它定义了一种搜索模式,可以用来匹配、查找、替换和提取文本中符合特定规则的字符串。Python 通过内置的 re 模块提供了对正则表达式的全面支持。掌握正则表达式的提取技巧,可以极大地提高文本处理的效率和准确性。

本文将深入探讨 Python 正则表达式的各种提取技巧,从基础概念到高级应用,辅以丰富的示例代码,帮助您轻松掌握高效的文本提取方法。

1. 正则表达式基础

在深入提取技巧之前,我们需要先了解正则表达式的基础知识。

1.1. 元字符

元字符是正则表达式中具有特殊含义的字符。它们是构建正则表达式的基本单元。

元字符 描述 示例
. 匹配除换行符外的任意单个字符 a.b 匹配 “aab”, “acb”, “a1b” 等
^ 匹配字符串的开头 ^abc 匹配以 “abc” 开头的字符串
$ 匹配字符串的结尾 xyz$ 匹配以 “xyz” 结尾的字符串
* 匹配前面的字符 0 次或多次 ab*c 匹配 “ac”, “abc”, “abbc” 等
+ 匹配前面的字符 1 次或多次 ab+c 匹配 “abc”, “abbc” 等,不匹配 “ac”
? 匹配前面的字符 0 次或 1 次 ab?c 匹配 “ac”, “abc”
{n} 匹配前面的字符恰好 n 次 a{3}b 匹配 “aaab”
{n,} 匹配前面的字符至少 n 次 a{2,}b 匹配 “aab”, “aaab”, “aaaab” 等
{n,m} 匹配前面的字符 n 到 m 次 a{1,3}b 匹配 “ab”, “aab”, “aaab”
[] 匹配方括号内的任意一个字符 [abc] 匹配 “a”, “b”, “c”
[^] 匹配不在方括号内的任意一个字符 [^abc] 匹配除 “a”, “b”, “c” 外的字符
\ 转义字符,用于匹配特殊字符 \.\* 匹配 “.*”
| 或,匹配两个或多个表达式中的一个 a|b 匹配 “a” 或 “b”
() 分组,将括号内的表达式作为一个整体 (ab)+ 匹配 “ab”, “abab”, “ababab” 等

1.2. 字符类

字符类是预定义的字符集合,用于简化正则表达式的书写。

字符类 描述 等价的 [] 表达式
\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]

1.3. 量词的贪婪与非贪婪模式

量词(*, +, ?, {n,m})默认是贪婪的,它们会尽可能多地匹配字符。在量词后面加上 ? 可以将其转换为非贪婪模式,使其尽可能少地匹配字符。

  • 贪婪模式: .* 会尽可能多的匹配字符。
  • 非贪婪模式: .*? 会尽可能少的匹配字符。

例如,对于字符串 “abbbbbc”,ab* (贪婪) 会匹配 “abbbbb”,而 ab*? (非贪婪) 会匹配 “a”。

2. re 模块常用函数

Python 的 re 模块提供了多个函数来使用正则表达式。

2.1. re.compile(pattern, flags=0)

将正则表达式编译成一个 Pattern 对象,可以重复使用,提高效率。

“`python
import re

pattern = re.compile(r’\d+’) # 编译一个匹配数字的正则表达式
result = pattern.findall(‘abc123def456’)
print(result) # 输出: [‘123’, ‘456’]
“`

2.2. re.search(pattern, string, flags=0)

在字符串中搜索第一个与正则表达式匹配的子串,返回一个 Match 对象,如果没有找到匹配项,则返回 None。

“`python
import re

match = re.search(r’\d+’, ‘abc123def’)
if match:
print(match.group()) # 输出: 123
“`

2.3. re.match(pattern, string, flags=0)

从字符串的开头开始匹配正则表达式,如果匹配成功,返回一个 Match 对象,否则返回 None。

“`python
import re

match = re.match(r’\d+’, ‘123def’)
if match:
print(match.group()) # 输出: 123

match = re.match(r’\d+’, ‘abc123def’)
if match:
print(match.group) #没有输出,因为不匹配
else:
print(“不匹配”)
“`

2.4. re.findall(pattern, string, flags=0)

查找字符串中所有与正则表达式匹配的子串,返回一个列表。

“`python
import re

result = re.findall(r’\d+’, ‘abc123def456ghi789’)
print(result) # 输出: [‘123’, ‘456’, ‘789’]
“`

2.5. re.finditer(pattern, string, flags=0)

查找字符串中所有与正则表达式匹配的子串,返回一个迭代器,每个元素是一个 Match 对象。

“`python
import re

iterator = re.finditer(r’\d+’, ‘abc123def456ghi789’)
for match in iterator:
print(match.group(), match.span())

输出:

123 (3, 6)

456 (9, 12)

789 (15, 18)

“`

2.6. re.split(pattern, string, maxsplit=0, flags=0)

根据正则表达式分割字符串,返回一个列表。

“`python
import re

result = re.split(r’\d+’, ‘abc123def456ghi’)
print(result) # 输出: [‘abc’, ‘def’, ‘ghi’]
“`

2.7. re.sub(pattern, repl, string, count=0, flags=0)

将字符串中所有与正则表达式匹配的子串替换为指定的字符串 repl

“`python
import re

result = re.sub(r’\d+’, ‘XXX’, ‘abc123def456ghi’)
print(result) # 输出: abcXXXdefXXXghi
“`

2.8. re.subn(pattern, repl, string, count=0, flags=0)

re.sub() 类似,但返回一个元组,包含替换后的字符串和替换次数。

“`python
import re

result = re.subn(r’\d+’, ‘XXX’, ‘abc123def456ghi’)
print(result) # 输出: (‘abcXXXdefXXXghi’, 2)
“`

2.9 Flags 参数

re 模块的很多函数都支持 flags 参数,用于修改正则表达式的行为。常用的 flags 包括:

  • re.IGNORECASEre.I: 忽略大小写。
  • re.MULTILINEre.M: 多行模式,^$ 匹配每一行的开头和结尾。
  • re.DOTALLre.S: 点号 . 匹配包括换行符在内的所有字符。
  • re.VERBOSEre.X: 详细模式, 可以写多行,并且可以添加注释。

“`python
import re
text = “””This is a
multi-line string.”””

使用 re.MULTILINE 标志

matches = re.findall(r’^\w+’, text, re.MULTILINE)
print(matches) # 输出: [‘This’, ‘multi’]

使用 re.VERBOSE

pattern = re.compile(r”’
\d+ # 匹配一个或多个数字
\s* # 匹配零个或多个空白字符
[a-z]+ # 匹配一个或多个小写字母
”’, re.VERBOSE | re.IGNORECASE) #同时使用多个flags

result = pattern.findall(“123 ABC”)
print(result) #输出: [‘123 ABC’]
“`

3. 提取技巧

掌握了基础知识和 re 模块的函数后,我们就可以学习各种提取技巧了。

3.1. 提取单个匹配项

使用 re.search()re.match() 可以提取第一个匹配项。通过 Match 对象的 group() 方法可以获取匹配到的内容。

“`python
import re

text = “My phone number is 123-456-7890.”
match = re.search(r’\d{3}-\d{3}-\d{4}’, text)
if match:
phone_number = match.group()
print(phone_number) # 输出: 123-456-7890
“`

3.2. 提取多个匹配项

使用 re.findall() 可以提取所有匹配项,返回一个列表。

“`python
import re

text = “I have 2 cats and 3 dogs.”
numbers = re.findall(r’\d+’, text)
print(numbers) # 输出: [‘2’, ‘3’]
“`

3.3. 使用分组提取

使用圆括号 () 可以将正则表达式的一部分分组,然后通过 Match 对象的 group(n) 方法获取第 n 个分组的内容(n 从 1 开始)。group(0) 表示整个匹配到的内容。

“`python
import re

text = “My email is [email protected].”
match = re.search(r'([\w.]+)@([\w.]+)’, text)
if match:
username = match.group(1)
domain = match.group(2)
print(f”Username: {username}”) # 输出: Username: john.doe
print(f”Domain: {domain}”) # 输出: Domain: example.com
“`

3.4. 使用命名分组提取

为了提高代码的可读性,可以使用命名分组 (?P<name>pattern),然后通过 Match 对象的 group('name') 方法获取分组的内容。

“`python
import re

text = “My email is [email protected].”
match = re.search(r'(?P[\w.]+)@(?P[\w.]+)’, text)
if match:
username = match.group(‘username’)
domain = match.group(‘domain’)
print(f”Username: {username}”) # 输出: Username: john.doe
print(f”Domain: {domain}”) # 输出: Domain: example.com
“`
使用命名分组可以让代码意图更明确。

3.5. 提取嵌套分组

正则表达式可以包含嵌套分组,group(n) 方法按照分组的左括号出现的顺序来确定分组的编号。

“`python
import re
text = “The price is (USD (100))”

match = re.search(r'((.?)\s+((.?)))’, text)

if match:
print(match.group(0)) #输出整个匹配: (USD (100))
print(match.group(1)) #输出第一个分组: USD
print(match.group(2)) #输出第二个分组: 100
“`

3.6. 非捕获分组

有时候,我们只想使用分组来组织正则表达式,而不需要捕获分组的内容。可以使用非捕获分组 (?:pattern),它不会被 group() 方法捕获。

“`python
import re

text = “I like apples and oranges.”
match = re.search(r'(?:apples|oranges)’, text)
if match:
print(match.group(0)) # 输出: apples
# print(match.group(1)) # 报错,因为没有捕获分组

“`
非捕获分组适用于不需要单独提取分组内容的情况,可以简化结果。

3.7. 处理重复的分组

当正则表达式中包含重复的分组时,group() 方法只会返回最后一次匹配到的内容。如果需要获取所有的匹配,可以使用 findall() 结合分组。

“`python
import re
text = “123-456-789”

错误的例子

match = re.search(r'(\d+-?)+’, text)
if match:
print(match.group(1)) # 输出: 789- #只捕获了最后一次

正确的例子: 使用 findall

result = re.findall(r'(\d+)-?’, text)
print(result) #输出: [‘123’, ‘456’, ‘789’]

“`

3.8 使用先行断言和后行断言

  • 先行断言 (Lookahead Assertion):

    • 正向先行断言: (?=pattern) 匹配 pattern 之前的文本, 但是不包含pattern本身。
    • 负向先行断言: (?!pattern) 匹配不以pattern开头的文本, 但是不包含pattern本身。
  • 后行断言 (Lookbehind Assertion): (注意,后行断言中的pattern必须是固定长度的)

    • 正向后行断言: (?<=pattern) 匹配 pattern 之后的文本, 但是不包含pattern本身。
    • 负向后行断言: (?<!pattern) 匹配不以 pattern 结尾的文本,但不包含pattern本身。

“`python
import re

提取所有后面跟着 “ing” 的单词

text = “I am reading and writing.”
result = re.findall(r’\w+(?=ing)’, text)
print(result) # 输出: [‘read’, ‘writ’]

提取所有前面是 “Mr. ” 的名字

text = “Mr. Smith and Mrs. Jones”
result = re.findall(r'(?<=Mr. )\w+’, text)
print(result) # 输出 [‘Smith’]

提取所有.txt文件,但不包含bad.txt

files = “good.txt, bad.txt, ok.txt”
result = re.findall(r'(?<!bad).txt\b’, files) #错误,因为?<!bad不是固定长度
result = re.findall(r'(?<!bad)\w+.txt\b’, files) #错误
result = re.findall(r'(?<!bad[.])\w+.txt\b’, files) #还是错误
result = re.findall(r'(?<!bad)\w+(?=.txt).txt\b’, files) #正确
print(result)

正确保后行断言

text = “apple banana cherry”
result = re.findall(r'(?<=apple )\w+’, text) # 正确:后行断言中的 “apple ” 长度固定
print(result) #输出:[‘banana’]

正确提取所有不是.log结尾的文件名

files = “data.txt, error.log, config.ini”
result = re.findall(r’\w+(?!.log)\b’, files)

应该输出 [‘data’, ‘config’] 但实际上 data.txt和config.ini都被切分了

print(result) #输出:[‘dat’, ‘tx’, ‘erro’, ‘lo’, ‘confi’, ‘in’]

result = re.findall(r'(\w+).(?!log\b)(\w+)’, files)
print(result) #输出:[(‘data’, ‘txt’), (‘config’, ‘ini’)]

result = re.findall(r'(\w+).(?:txt|ini)\b’, files) #更优
print(result) #输出:[‘data’, ‘config’]

“`

先行断言和后行断言在需要根据上下文来提取内容时非常有用,它们可以实现更精确的匹配。

4. 常见应用场景

正则表达式提取技巧在实际应用中非常广泛,以下是一些常见的应用场景:

  • 数据清洗: 从非结构化文本中提取关键信息,如日期、时间、电子邮件地址、电话号码等。
  • 日志分析: 从日志文件中提取错误信息、IP 地址、访问时间等。
  • 网络爬虫: 从 HTML 页面中提取链接、标题、文本内容等。
  • 表单验证: 验证用户输入的格式是否符合要求。
  • 代码分析: 从代码中提取函数名、变量名、注释等。
  • 配置文件解析: 从配置文件中提取配置项和值。
  • 自然语言处理 (NLP): 文本预处理,分词,提取特定词性的词。

5. 性能优化

正则表达式的性能可能会受到多种因素的影响,以下是一些优化建议:

  • 编译正则表达式: 对于需要重复使用的正则表达式,使用 re.compile() 进行编译可以提高效率。
  • 避免不必要的分组: 如果不需要捕获分组的内容,使用非捕获分组 (?:pattern)
  • 使用更具体的字符类: 例如,使用 \d 代替 [0-9]
  • 避免回溯: 过于复杂的正则表达式可能会导致回溯,降低性能。尽量简化正则表达式,避免使用嵌套的量词。
  • 使用非贪婪模式: 在适当的情况下,使用非贪婪模式可以减少匹配次数。
  • 测试和分析: 使用 timeit 模块等工具测试正则表达式的性能,找出瓶颈并进行优化。

总结

Python 正则表达式提供了强大的文本处理能力,掌握其提取技巧可以极大地提高工作效率。本文从基础概念入手,详细介绍了 re 模块的常用函数、各种提取技巧、常见应用场景以及性能优化建议。希望通过本文的学习,您能够熟练运用正则表达式,轻松应对各种文本处理任务。记住,实践是最好的老师,多写多练,才能真正掌握正则表达式的精髓。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部