You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python字典遍历性能优化:700万行文件频次统计慢的问题

问题分析与优化方案

嘿,你的问题我仔细看了——700万行的文件跑几小时确实不太正常,核心问题不在于文件读取方式本身,而是内存爆仓+无意义的IO操作拖慢了整个流程。下面我拆解一下现有代码的坑点,再给你一套高效的优化方案:

现有代码的核心问题

  • 一次性加载全量数据到内存:list(open(file_in, 'r'))直接把700万行读进列表,再做strip处理,这会占用巨量内存,甚至可能触发系统虚拟内存交换(把内存数据写到硬盘上),速度直接暴跌。
  • 字典存储冗余数据:你把每个key对应的所有行都存在字典里,700万行的话,字典会大到离谱,遍历的时候缓存根本扛不住,自然越跑越慢。
  • 高频次控制台打印:每处理一行就打印一次,700万次的控制台IO是超级耗时的,这绝对是拖慢速度的头号元凶。
  • 低效的key判断:keyf not in dict_in.keys()其实没必要取keys视图,直接keyf not in dict_in更快,因为字典的in操作本身就是O(1)的。

优化思路

  1. 流式处理,不一次性加载全量数据:逐行读取处理,内存里只存当前行,大幅降低内存占用。
  2. 分两次遍历文件:第一次只统计每个key的出现次数,不存所有行;第二次根据统计好的次数,把行写到对应文件。这样内存只需要存key和计数,而不是几百万行文本。
  3. 砍掉不必要的打印:控制台打印速度远慢于内存操作,除非调试,否则完全可以去掉,或者改成每10万行打印一次进度,既不影响速度又能看到进展。
  4. 用defaultdict简化计数:比手动判断key是否存在更简洁高效。
  5. 用with语句管理文件:自动处理文件打开关闭,避免资源泄漏,代码也更干净。

优化后的代码

from collections import defaultdict

def count_key_frequencies(input_file):
    key_counts = defaultdict(int)
    # 第一次遍历:只统计每个key的出现次数
    with open(input_file, 'r', encoding='utf-8') as f:
        for line_num, line in enumerate(f, 1):
            stripped_line = line.strip()
            if not stripped_line:  # 跳过空行,按需调整
                continue
            # 处理行长度不足的情况,避免切片报错
            if len(stripped_line) >= 69:
                keyf = stripped_line[10:69]
                key_counts[keyf] += 1
            # 每10万行打印一次进度,避免频繁IO
            if line_num % 100000 == 0:
                print(f"已处理 {line_num} 行")
    return key_counts

def split_lines_by_frequency(input_file, output_single, output_double, output_multi, key_counts):
    # 第二次遍历:根据计数写入对应文件
    with open(input_file, 'r', encoding='utf-8') as in_f, \
         open(output_single, 'w', encoding='utf-8') as out1, \
         open(output_double, 'w', encoding='utf-8') as out2, \
         open(output_multi, 'w', encoding='utf-8') as out3:
        for line in in_f:
            stripped_line = line.strip()
            if not stripped_line:
                continue
            if len(stripped_line) >= 69:
                keyf = stripped_line[10:69]
                count = key_counts.get(keyf, 0)
                if count == 1:
                    out1.write(stripped_line + '\n')
                elif count == 2:
                    out2.write(stripped_line + '\n')
                elif count > 2:
                    out3.write(stripped_line + '\n')

# 主程序入口
if __name__ == "__main__":
    # 替换成你的实际文件路径
    INPUT_FILE = "your_input_file.txt"
    OUTPUT_1 = "frequency_1.txt"
    OUTPUT_2 = "frequency_2.txt"
    OUTPUT_MORE = "frequency_more_than_2.txt"
    
    print("开始统计key出现频次...")
    key_counts = count_key_frequencies(INPUT_FILE)
    print("开始按频次拆分文件...")
    split_lines_by_frequency(INPUT_FILE, OUTPUT_1, OUTPUT_2, OUTPUT_MORE, key_counts)
    print("所有处理完成!")

额外小贴士

  • 处理短行情况:我加了len(stripped_line) >= 69的判断,避免行长度不够时切片报错,你可以根据实际情况调整。
  • 指定编码:打开文件时加上encoding='utf-8'(或你文件的实际编码),避免系统默认编码导致的乱码或读取错误。
  • 增大缓冲区:如果想要更快的IO,可以给open加上buffering=1024*1024(1MB缓冲区),减少磁盘读写次数。
  • 极端内存情况:如果key的数量特别多(比如大部分key都是唯一的),可以把第一次统计的结果写入临时文件,第二次遍历时读取临时文件的计数,不过700万行的话,普通字典应该完全能hold住。

这样调整后,内存占用会降到原来的几十分之一,速度至少能提升几十倍,不会再跑几小时了。

内容的提问来源于stack exchange,提问作者onlyf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:00:02