提升Python正则表达式批量提取文件名信息的匹配速度及正则语法优化方案问询
看起来你已经搞定了基础的文件名信息提取逻辑,但担心未来处理几千个文件夹时的效率问题,还有正则匹配固定位数数字的灵活性限制对吧?我来给你梳理几个实用的优化方向,帮你把代码变得更高效、更灵活:
一、正则语法优化:兼容可变长度的数字
你当前的正则用了\d{4}、\d{5}这类固定位数的匹配规则,万一以后数字长度变化(比如B后面变成5位数字),这些规则就直接失效了。咱们可以调整成更通用的模式:
- B_ID:匹配
B开头+任意数量的数字,用r'B\d+'(\d+表示1个或多个数字) - G_ID:匹配
G开头+数字+-+数字,用r'G\d+-\d+' - 温度:匹配数字+
C,兼容1位或多位数字,用r'\d+C'
更高效的是把三个字段合并成一个正则表达式,用捕获组一次性提取所有目标信息,这样只需要调用一次正则匹配,减少重复开销:
pattern = re.compile(r'(B\d+)_(G\d+-\d+)_(\d+C)')
这个模式刚好对应你文件名的结构:Bxxx_Gxxx-xxx_xxC_...,用下划线分隔三个核心字段,完美匹配。
二、代码效率优化:针对大规模文件的性能提升
你的现有代码有几个可以优化的点,处理几千个文件时,这些调整能显著降低运行时间:
1. 预编译正则表达式
每次调用re.search时,Python都会重新编译正则模式,几千次循环下来这浪费的时间很可观。提前编译好表达式,就能重复使用,省掉大量重复计算:
# 在循环或函数外预编译,只做一次 pattern = re.compile(r'(B\d+)_(G\d+-\d+)_(\d+C)')
2. 用Path原生属性替代os.path.basename
Path对象本身就提供了name(带扩展名的文件名)和stem(不带扩展名的文件名)属性,不需要跨模块调用os.path.basename,更高效:
# 原来的names = [os.path.basename(path) for path in paths] # 改成 names = [path.name for path in paths]
3. 单次匹配提取所有字段,减少正则调用次数
原来的循环里每次调用三次re.search,改成一次匹配拿到三个结果,直接降低2/3的正则引擎调用开销:
for name in names: match = pattern.search(name) if match: # 增加判断,避免无匹配时报错 B_ID = match.group(1) G_ID = match.group(2) Temperature = match.group(3) print(f'B_ID is: {B_ID}') print(f'G_ID is: {G_ID}') print(f'The temperature is: {Temperature}')
4. 用生成器替代列表,节省内存
如果要处理几千个文件夹,一次性把所有文件路径存成列表会占用不少内存。改成生成器逐个返回文件路径,内存占用会低很多:
def get_datafiles_in_subfolder(root: Path): for file in root.rglob('*.csv'): if file.is_file(): yield file # 生成器,用的时候才返回下一个路径
遍历的时候直接用:
for path in get_datafiles_in_subfolder(data_root): filename = path.name # ... 处理匹配逻辑
5. 简化文件查找逻辑
原来的filter+list可以改成更简洁高效的列表推导式:
# 原来的paths = list(filter(lambda file: file.is_file(), root.rglob('*.csv'))) # 改成 paths = [file for file in root.rglob('*.csv') if file.is_file()]
三、完整优化后的代码示例
from pathlib import Path import re import time start_time = time.time() # 预编译正则表达式,一次性匹配所有需要的字段 pattern = re.compile(r'(B\d+)_(G\d+-\d+)_(\d+C)') def get_datafiles_in_subfolder(root: Path): # 用生成器返回文件路径,节省内存 for file in root.rglob('*.csv'): if file.is_file(): yield file data_root = Path(r'C:\Desktop\Users\myname\TestData') for path in get_datafiles_in_subfolder(data_root): filename = path.name match_result = pattern.search(filename) if match_result: b_id, g_id, temp = match_result.groups() print(f'B_ID is: {b_id}') print(f'G_ID is: {g_id}') print(f'The temperature is: {temp}') end_time = time.time() print(f"The program's run time is {end_time - start_time:.6f}s")
四、性能提升的核心原因
- 预编译正则:避免了每次循环重复编译正则的开销,处理大量文件时效果尤其明显
- 单次正则匹配:从三次调用减少到一次,降低了正则引擎的运行次数
- 生成器替代列表:不需要一次性加载所有文件路径到内存,适合大规模文件场景
- Path原生属性:避免跨模块调用的额外开销,比
os.path.basename更快
五、其他可选工具/方法
如果未来需要结合数据分析,可以考虑用pandas批量处理:
import pandas as pd files = list(data_root.rglob('*.csv')) df = pd.DataFrame({'file_path': files}) df['filename'] = df['file_path'].apply(lambda x: x.name) # 一次性提取所有字段到DataFrame df[['B_ID', 'G_ID', 'Temperature']] = df['filename'].str.extract(r'(B\d+)_(G\d+-\d+)_(\d+C)')
这种方式适合后续需要对提取的信息做统计分析的场景,但单纯提取文件名信息的话,正则+pathlib的组合更轻量高效。
备注:内容来源于stack exchange,提问作者CYU1

