Python 正则表达式 re.findall()
re.findall()
是 Python re
模块中用于查找字符串中所有匹配正则表达式模式的子串的方法。与 re.search()
只返回第一个匹配项不同,findall()
会返回所有匹配项。
基本语法
re.findall(pattern, string, flags=0)
参数说明:
pattern
: 正则表达式模式string
: 要搜索的字符串flags
: 可选标志,如re.IGNORECASE
、re.MULTILINE
等
返回值:
- 如果模式中有分组,返回分组元组的列表
- 如果模式中没有分组,返回所有匹配子串的列表
- 如果没有找到匹配,返回空列表
1. 基本查找用法
示例1:查找所有数字
import retext = "There are 3 apples and 5 oranges"
numbers = re.findall(r'\d+', text)
print(numbers) # 输出: ['3', '5']
示例2:查找所有单词
text = "Hello world! Python is awesome."
words = re.findall(r'\w+', text)
print(words) # 输出: ['Hello', 'world', 'Python', 'is', 'awesome']
2. 使用分组时的行为
示例3:无分组情况
text = "John: 30, Jane: 25, Bob: 42"
ages = re.findall(r': \d+', text)
print(ages) # 输出: [': 30', ': 25', ': 42']
示例4:有分组情况(返回分组内容)
text = "John: 30, Jane: 25, Bob: 42"
ages = re.findall(r': (\d+)', text)
print(ages) # 输出: ['30', '25', '42']
示例5:多个分组情况
text = "John: 30, Jane: 25, Bob: 42"
info = re.findall(r'(\w+): (\d+)', text)
print(info) # 输出: [('John', '30'), ('Jane', '25'), ('Bob', '42')]
3. 使用正则表达式高级特性
示例6:非贪婪匹配
html = "<div>content1</div><div>content2</div>"
contents = re.findall(r'<div>(.*?)</div>', html)
print(contents) # 输出: ['content1', 'content2']
示例7:使用字符类
text = "Colors: red, green, BLUE, Yellow"
colors = re.findall(r'[a-zA-Z]+', text)
print(colors) # 输出: ['Colors', 'red', 'green', 'BLUE', 'Yellow']
示例8:使用边界匹配
text = "cat category concatenate"
words = re.findall(r'\bcat\b', text)
print(words) # 输出: ['cat']
4. 使用标志(flags)
示例9:忽略大小写
text = "Apple orange BANANA Grape"
fruits = re.findall(r'[a-z]+', text, re.IGNORECASE)
print(fruits) # 输出: ['Apple', 'orange', 'BANANA', 'Grape']
示例10:多行模式
text = """First line
Second line
Third line"""
lines = re.findall(r'^\w+', text, re.MULTILINE)
print(lines) # 输出: ['First', 'Second', 'Third']
5. 实际应用场景
示例11:提取电子邮件地址
text = "Contact us at info@example.com or support@test.org"
emails = re.findall(r'[\w\.-]+@[\w\.-]+', text)
print(emails) # 输出: ['info@example.com', 'support@test.org']
示例12:提取URL链接
text = "Visit https://www.example.com or http://test.org"
urls = re.findall(r'https?://[^\s/$.?#].[^\s]*', text)
print(urls) # 输出: ['https://www.example.com', 'http://test.org']
示例13:解析日志文件
log = """
2023-04-15 10:00:00 INFO System started
2023-04-15 10:01:23 ERROR Database connection failed
2023-04-15 10:02:45 WARNING Disk space low
"""
errors = re.findall(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} ERROR (.+)', log)
print(errors) # 输出: ['Database connection failed']
示例14:提取HTML标签内容
html = "<h1>Title</h1><p>Paragraph 1</p><p>Paragraph 2</p>"
paragraphs = re.findall(r'<p>(.*?)</p>', html)
print(paragraphs) # 输出: ['Paragraph 1', 'Paragraph 2']
示例15:提取电话号码
text = "Call 123-456-7890 or 987.654.3210"
phones = re.findall(r'\d{3}[-.]\d{3}[-.]\d{4}', text)
print(phones) # 输出: ['123-456-7890', '987.654.3210']
示例16:提取货币金额
text = "Prices: $12.99, €9.99, ¥1000"
prices = re.findall(r'[\$€¥]\d+\.?\d*', text)
print(prices) # 输出: ['$12.99', '€9.99', '¥1000']
示例17:提取日期
text = "Dates: 2023-04-15, 15/04/2023, 04.15.2023"
dates = re.findall(r'\d{4}[-/.]\d{2}[-/.]\d{2}|\d{2}[-/.]\d{2}[-/.]\d{4}', text)
print(dates) # 输出: ['2023-04-15', '15/04/2023', '04.15.2023']
6. 高级技巧
示例18:使用命名分组
text = "User: john_doe, Age: 30; User: jane_smith, Age: 25"
users = re.findall(r'User: (?P<name>\w+), Age: (?P<age>\d+)', text)
print(users) # 输出: [('john_doe', '30'), ('jane_smith', '25')]
示例19:复杂模式匹配
text = "Coordinates: (12.345, -67.890), (0, 42.123)"
coords = re.findall(r'\(([^,]+),\s*([^)]+)\)', text)
print(coords) # 输出: [('12.345', '-67.890'), ('0', '42.123')]
示例20:使用正向预查
text = "apple orange banana grape"
fruits_before_banana = re.findall(r'\w+(?=\sbanana)', text)
print(fruits_before_banana) # 输出: ['orange']
7. 注意事项
re.findall()
返回的是字符串列表或元组列表,不是匹配对象- 当模式中有分组时,只返回分组内容,不是整个匹配
- 对于大文本,考虑使用
re.finditer()
节省内存 - 复杂的正则表达式可能会影响性能
- 处理HTML/XML等结构化数据时,最好使用专门的解析器
示例21:与 re.finditer()
对比
text = "Error 404, Error 500, Warning 302"# 使用 findall
codes = re.findall(r'Error (\d+)', text)
print(codes) # 输出: ['404', '500']# 使用 finditer
for match in re.finditer(r'Error (\d+)', text):print(f"Found at {match.start()}-{match.end()}: {match.group(1)}")
# 输出:
# Found at 0-8: 404
# Found at 10-18: 500
8. 性能优化建议
-
预编译常用正则表达式:
pattern = re.compile(r'\d+') numbers = pattern.findall(text)
-
尽量使用具体模式而非宽泛模式:
# 不好的写法 re.findall(r'.*:\s*(.*)', text)# 更好的写法 re.findall(r'\w+:\s*(\w+)', text)
-
避免过度使用回溯:
# 可能导致性能问题的写法 re.findall(r'(a+)+$', text)
Python re.findall()
方法中的 flags 参数详解
re.findall()
方法的 flags
参数可以修改正则表达式的匹配行为,使其更灵活地适应不同的文本处理需求。下面我将详细介绍各种 flag 的用法和实际应用场景。
1. 常用 flags 概览
标志常量 | 简写 | 描述 |
---|---|---|
re.IGNORECASE | re.I | 忽略大小写 |
re.MULTILINE | re.M | 多行模式,影响 ^ 和 $ |
re.DOTALL | re.S | 使 . 匹配包括换行符在内的所有字符 |
re.VERBOSE | re.X | 允许编写更易读的正则表达式 |
re.ASCII | re.A | 使 \w , \W , \b , \B , \d , \D , \s , \S 只匹配 ASCII 字符 |
re.LOCALE | re.L | 使 \w , \W , \b , \B 依赖当前区域设置 |
re.UNICODE | re.U | 使 \w , \W , \b , \B , \d , \D , \s , \S 匹配 Unicode 字符 |
2. 各 flag 详细说明及示例
2.1 re.IGNORECASE
(re.I) - 忽略大小写
作用:使匹配对大小写不敏感
示例:
import retext = "Apple banana ORANGE Grape"
# 不使用 IGNORECASE
result = re.findall(r'apple', text)
print(result) # []# 使用 IGNORECASE
result = re.findall(r'apple', text, re.IGNORECASE)
print(result) # ['Apple']# 匹配所有水果名(忽略大小写)
fruits = re.findall(r'[a-z]+', text, re.I)
print(fruits) # ['Apple', 'banana', 'ORANGE', 'Grape']
2.2 re.MULTILINE
(re.M) - 多行模式
作用:改变 ^
和 $
的行为,使它们分别匹配每一行的开头和结尾
示例:
text = """First line
Second line
Third line"""# 不使用 MULTILINE
result = re.findall(r'^\w+', text)
print(result) # ['First']# 使用 MULTILINE
result = re.findall(r'^\w+', text, re.MULTILINE)
print(result) # ['First', 'Second', 'Third']# 匹配每行末尾的单词
result = re.findall(r'\w+$', text, re.M)
print(result) # ['line', 'line', 'line']
2.3 re.DOTALL
(re.S) - 点号匹配所有模式
作用:使 .
匹配包括换行符在内的所有字符
示例:
text = """Start
Middle
End"""# 不使用 DOTALL
result = re.findall(r'Start.*End', text)
print(result) # []# 使用 DOTALL
result = re.findall(r'Start.*End', text, re.DOTALL)
print(result) # ['Start\nMiddle\nEnd']# 提取多行注释内容
html = """<!--
这是多行
HTML注释
-->"""
comment = re.findall(r'<!--(.*?)-->', html, re.DOTALL)
print(comment) # [' \n这是多行\nHTML注释 \n']
2.4 re.VERBOSE
(re.X) - 详细模式
作用:允许在正则表达式中添加空白和注释,使其更易读
示例:
# 复杂的电话号码正则表达式
phone_re = re.compile(r'''^(\+\d{1,3})? # 国际区号[-\s]? # 分隔符(\d{3}) # 前3位[-\s]? # 分隔符(\d{3,4}) # 中间3或4位[-\s]? # 分隔符(\d{4}) # 最后4位$''', re.VERBOSE)text = "Phone numbers: +86-138-1234-5678, 010-87654321"
numbers = phone_re.findall(text)
print(numbers) # [('+86', '138', '1234', '5678'), ('', '010', '8765', '4321')]
2.5 re.ASCII
(re.A) - ASCII 模式
作用:使 \w
, \W
, \b
, \B
, \d
, \D
, \s
, \S
只匹配 ASCII 字符
示例:
text = "Python3 中文 Español café"# 默认模式(Unicode)
result = re.findall(r'\w+', text)
print(result) # ['Python3', '中文', 'Español', 'café']# ASCII 模式
result = re.findall(r'\w+', text, re.ASCII)
print(result) # ['Python3', 'Espa', 'ol', 'caf']
2.6 re.UNICODE
(re.U) - Unicode 模式
作用:使 \w
, \W
, \b
, \B
, \d
, \D
, \s
, \S
匹配 Unicode 字符(Python 3 默认)
示例:
text = "Русский 中文 ελληνικά"# 默认就是 UNICODE 模式
result = re.findall(r'\w+', text)
print(result) # ['Русский', '中文', 'ελληνικά']# 显式指定 UNICODE
result = re.findall(r'\w+', text, re.UNICODE)
print(result) # ['Русский', '中文', 'ελληνικά']
2.7 re.LOCALE
(re.L) - 区域设置模式
作用:使 \w
, \W
, \b
, \B
依赖当前区域设置(不推荐使用)
示例:
import locale# 设置区域为德语
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')text = "straße café"
result = re.findall(r'\w+', text, re.LOCALE)
print(result) # ['straße', 'café']
3. 组合使用多个 flags
可以通过按位或 (|
) 操作符组合多个 flags
示例:
text = """Name: John
AGE: 30
name: Jane
age: 25"""# 同时使用 IGNORECASE 和 MULTILINE
results = re.findall(r'^name:\s*(\w+)', text, re.I | re.M)
print(results) # ['John', 'Jane']# 解析多行配置项
config = """[Server]
host = example.com
port = 8080
timeout = 30"""settings = re.findall(r'^(\w+)\s*=\s*(.*)$', config, re.MULTILINE | re.VERBOSE
)
print(settings) # [('host', 'example.com'), ('port', '8080'), ('timeout', '30')]
4. 实际应用场景
4.1 解析日志文件(多行模式)
log = """2023-04-15 10:00:00 [INFO] System started
2023-04-15 10:01:23 [ERROR] Database connection failed
2023-04-15 10:02:45 [WARN] Disk space low"""# 提取所有错误日志(带时间戳)
errors = re.findall(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[ERROR\] (.*)$',log,re.MULTILINE
)
print(errors) # [('2023-04-15 10:01:23', 'Database connection failed')]
4.2 提取HTML内容(点号匹配所有)
html = """<div><p>First paragraph</p><p>Second paragraph</p>
</div>"""# 提取所有段落内容
paragraphs = re.findall(r'<p>(.*?)</p>',html,re.DOTALL # 使 . 匹配换行符
)
print(paragraphs) # ['First paragraph', 'Second paragraph']
4.3 多语言文本处理(Unicode模式)
text = "English: hello, 中文: 你好, Français: bonjour"# 提取所有非ASCII单词
words = re.findall(r'[^\x00-\x7F]+', # 匹配非ASCII字符text,re.UNICODE
)
print(words) # ['你好', 'bonjour']
4.4 复杂模式匹配(详细模式)
# 匹配各种格式的日期
date_re = re.compile(r'''^(?:20\d{2}|19\d{2}) # 年份 1900-2099[-/.] # 分隔符(?:0[1-9]|1[0-2]) # 月份 01-12[-/.] # 分隔符(?:0[1-9]|[12][0-9]|3[01]) # 日 01-31$''', re.VERBOSE)dates = ["2023-04-15", "1999/12/31", "2000.01.01"]
valid_dates = [d for d in dates if date_re.search(d)]
print(valid_dates) # ['2023-04-15', '1999/12/31', '2000.01.01']
5. 注意事项
- flag 的作用范围:flags 会影响整个正则表达式的行为
- flag 的组合:多个 flags 可以组合使用,但要注意它们之间的交互
- 性能考虑:某些 flags(如
re.UNICODE
)可能会影响性能 - 预编译正则表达式:频繁使用的正则表达式应该先编译再使用
pattern = re.compile(r'your_pattern', flags=re.I | re.M) results = pattern.findall(text)
- Python 3 的默认行为:在 Python 3 中,
re.UNICODE
是默认启用的
通过合理使用这些 flags,你可以编写出更强大、更灵活的正则表达式,适应各种复杂的文本处理需求。