如何快速移除大文件中的多份XML声明?Python优化方案咨询
问题描述
我有一批本地存储的大文件,单个文件约含1100万行(450MB),文件内存在多份XML声明及指定DOCTYPE内容。目前我通过Python将文件视为.txt文件,逐行过滤掉这些内容后写入新文件,但单个文件处理耗时约20分钟,效率极低,寻求更高效的实现方案。
当前代码
month_file = "2015-01.nml.txt" delete_lines = [ '<?xml version="1.0" encoding="ISO-8859-1" ?>', '<?xml version="1.0" encoding="iso-8859-1" ?>', '<!DOCTYPE doc SYSTEM "djnml-1.0b.dtd">', ] with open(month_file, encoding="ISO-8859-1") as in_fh: while True: line = in_fh.readline() if not line: break if any(x in line for x in delete_lines): continue else: out_fh = open('myfile_faster.xml', "a") out_fh.write(line) out_fh.close()
低效原因分析
- 频繁打开/关闭输出文件:循环内每次写入都重新打开输出文件,文件IO是系统中最慢的操作之一,频繁的打开关闭会产生巨大性能开销,这是导致速度慢的核心原因。
- 逐行读取的方式不够高效:
readline()循环的性能不如Python文件对象的原生迭代器。 - 字符串匹配效率低:
any(x in line for x in delete_lines)每次遍历列表做子串匹配,若目标是整行匹配,用集合查找会更快。
优化方案
1. 核心优化:将输出文件放在循环外打开
这是最立竿见影的优化,避免频繁IO操作:
month_file = "2015-01.nml.txt" output_file = 'myfile_faster.xml' # 用集合存储待过滤行,查找速度远快于列表 delete_lines = { '<?xml version="1.0" encoding="ISO-8859-1" ?>', '<?xml version="1.0" encoding="iso-8859-1" ?>', '<!DOCTYPE doc SYSTEM "djnml-1.0b.dtd">', } # 同时打开输入和输出文件,循环内仅做写入操作 with open(month_file, encoding="ISO-8859-1") as in_fh, open(output_file, 'w', encoding="ISO-8859-1") as out_fh: # 用文件迭代器遍历,比readline()更高效 for line in in_fh: # 去除换行符后匹配(适配文件行尾的换行差异) stripped_line = line.rstrip('\n') if stripped_line not in delete_lines: out_fh.write(line)
2. 批量读取进一步提升效率
如果内存充足(450MB文件对现代机器无压力),采用批量读取减少IO次数:
month_file = "2015-01.nml.txt" output_file = 'myfile_faster.xml' delete_lines = { '<?xml version="1.0" encoding="ISO-8859-1" ?>', '<?xml version="1.0" encoding="iso-8859-1" ?>', '<!DOCTYPE doc SYSTEM "djnml-1.0b.dtd">', } # 定义批量读取块大小(比如64MB,可根据内存调整) BLOCK_SIZE = 64 * 1024 * 1024 with open(month_file, encoding="ISO-8859-1") as in_fh, open(output_file, 'w', encoding="ISO-8859-1") as out_fh: while True: block = in_fh.read(BLOCK_SIZE) if not block: break # 按换行分割块,处理每一行 lines = block.split('\n') filtered_lines = [line for line in lines if line not in delete_lines] # 拼接后写入,注意补全换行符 out_fh.write('\n'.join(filtered_lines) + '\n')
3. 正则匹配处理复杂场景(可选)
如果待过滤行存在空格、大小写等细微差异,预编译正则表达式提升匹配速度:
import re month_file = "2015-01.nml.txt" output_file = 'myfile_faster.xml' # 预编译正则,忽略大小写和前后空格,匹配目标行 pattern = re.compile( r'^\s*(<\?xml version="1\.0" encoding="iso-8859-1"\s*\?>|<!DOCTYPE doc SYSTEM "djnml-1\.0b\.dtd">)\s*$', re.IGNORECASE ) with open(month_file, encoding="ISO-8859-1") as in_fh, open(output_file, 'w', encoding="ISO-8859-1") as out_fh: for line in in_fh: if not pattern.match(line): out_fh.write(line)
4. 二进制模式读写(极致优化)
若无需处理编码转换,用二进制模式跳过文本编码/解码步骤,速度会大幅提升:
month_file = "2015-01.nml.txt" output_file = 'myfile_faster.xml' # 转换为字节串,包含换行符(适配文件行尾) delete_bytes = { b'<?xml version="1.0" encoding="ISO-8859-1" ?>\n', b'<?xml version="1.0" encoding="iso-8859-1" ?>\n', b'<!DOCTYPE doc SYSTEM "djnml-1.0b.dtd">\n', # 兼容无换行的情况 b'<?xml version="1.0" encoding="ISO-8859-1" ?>', b'<?xml version="1.0" encoding="iso-8859-1" ?>', b'<!DOCTYPE doc SYSTEM "djnml-1.0b.dtd">', } with open(month_file, 'rb') as in_fh, open(output_file, 'wb') as out_fh: for line in in_fh: if line not in delete_bytes: out_fh.write(line)
效果预期
仅修复“频繁打开输出文件”这一问题,处理速度即可提升几十倍,单个文件处理时间可降至几分钟内;结合批量读取或二进制模式,速度还能进一步优化。
内容的提问来源于stack exchange,提问作者Nishant Das
相关产品推荐
相关产品推荐

