请教 Python 大佬,遍历一个 5GB 大小的 txt 文件,用什么方式效率比较高 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
ranxi
V2EX    程序员

请教 Python 大佬,遍历一个 5GB 大小的 txt 文件,用什么方式效率比较高

  •  
  •   ranxi 2022 年 7 月 22 日 5355 次点击
    这是一个创建于 1268 天前的主题,其中的信息可能已经有所发展或是发生改变。

    python 新手,现在有个需求,大概几百万条数据需要翻译,映射关系存在一个 5GB 左右大小的 txt 文件里,也就是拿几百万个值去几亿条数据里去匹配,怎么做比较好

    35 条回复    2022-07-25 11:10:23 +08:00
    jarence
        1
    jarence  
       2022 年 7 月 22 日
    介绍的太笼统了,匹配是怎么匹配的?
    HankLu
        2
    HankLu  
       2022 年 7 月 22 日
    社工库?
    Richard14
        3
    Richard14  
       2022 年 7 月 22 日
    5GB 不大,全部载入内存也能抗
    如果是 500G 的话就要建立索引了,可以考虑使用数据库。不过无论如何如果要大量随机读写的话效率不会很美丽就是了
    rushpu
        4
    rushpu  
       2022 年 7 月 22 日
    可以用 dask.bag.read_text
    https://examples.dask.org/bag.html
    VincentYoung
        5
    VincentYoung  
       2022 年 7 月 22 日 via iPhone
    之前搞过这个。你不要 readlines 会卡死,readline 就行。建议切成 5 个 1G 的文件,这样效率更高。
    VincentYoung
        6
    VincentYoung 
       2022 年 7 月 22 日 via iPhone
    用的数据库是 clickhouse ,查询速度还是挺快。
    ranxi
        7
    ranxi  
    OP
       2022 年 7 月 22 日
    @jarence 两个文件 O1 和 O2 ,可以理解成两张表,每行\t 分隔,O1 每行是 a ,b ,c ,其中 c 需要映射成另一个值 d ,这个映射关系在 O2 文件里( c ,d ),O2 文件大概有 5GB ,几亿行数据。
    ranxi
        8
    ranxi  
    OP
       2022 年 7 月 22 日
    @Richard14 试了下 pandas ,查一次要 3 ,4 秒,查几百万次完全查不动啊
    ranxi
        9
    ranxi  
    OP
       2022 年 7 月 22 日
    暂不考虑数据库
    Juszoe
        10
    Juszoe  
       2022 年 7 月 22 日
    对 O2 建哈希表完事了,5 个 G 绰绰有余
    liuhan907
        11
    liuhan907  
       2022 年 7 月 22 日
    这么朴素的任务弄个 hash 表不就完了。啥你说内存不够,不够就加。内存多便宜.
    blankmiss
        12
    blankmiss  
       2022 年 7 月 22 日
    @ranxi 给个模拟数据
    ytmsdy
        13
    ytmsdy  
       2022 年 7 月 22 日
    清洗出来,自己先按照数据的逻辑做一个大的基本划分,然后丢到 pandas !
    SenLief
        14
    SenLief  
       2022 年 7 月 22 日
    多加几根内存条就可以吧? 5g 不是很大
    F281M6Dh8DXpD1g2
        15
    F281M6Dh8DXpD1g2  
       2022 年 7 月 22 日 via iPhone
    整到 sqlite 里
    一个 join 完事
    有你问的时间都跑完了
    ranxi
        16
    ranxi  
    OP
       2022 年 7 月 22 日
    @liprais 用不了 sql
    ranxi
        17
    ranxi  
    OP
       2022 年 7 月 22 日
    O1 按需求是要逐行读取的,所以只能对 O2 操作。我明天试试 hash 表吧
    wxf666
        18
    wxf666  
       2022 年 7 月 22 日   1
    @ranxi 按理说你都能用 python 了,sqlite 是内置的标准库,应该是用得了的

    # 根据 O2 构造 hash 表(字典)
    with open(r'O2.txt', encoding='utf-8') as fp:
       table = dict((row.split('\t') for row in fp))

    # O1 逐行替换并输出
    with open(r'O1.txt', encoding='utf-8') as in_fp, open(r'out.txt', 'w', encoding='utf-8') as out_fp:
       for row in in_fp:
         cols = row.split('\t')
         cols[2] = table[cols[2].rstrip('\n')]
         out_fp.write('\t'.join(cols))

    内存若不够,考虑构造 hash 表时,仅将 hash(str) 作为键名,然后 cols[2] = table[hash(cols[2].rstrip('\n'))] ?
    Trim21
        19
    Trim21  
       2022 年 7 月 23 日 via Android
    说不定 python 编译的时候没带 SQLite
    renmu123
        20
    renmu123  
       2022 年 7 月 23 日 via Android
    扔哈希或者 Redis 里再查
    wxf666
        21
    wxf666  
       2022 年 7 月 23 日   2
    @ranxi 可以试试用 sqlite ,几行搞定 导入+翻译+输出,感觉速度应该也不慢


    1. 生成 O2.txt (从小写字母,映射至大写字母,共 26 行)

    printf "%s\n" {a..z} | sed 's/^.*$/&\t\U&/' | tee O2.txt

    『输出』
    a    A
    b    B
    …  …
    z    Z


    2. 生成 O1.txt (也是 26 行,字段内容是:主键 ID 、预期转换成啥样、待转换内容)

    printf "%s\n" {a..z} | awk -v OFS=$'\t' '{print NR, toupper($0), $0}' | tee O1.txt

    『输出』
    1    A    a
    2    B    b
    …  …  …
    26    Z    z


    3. 导入映射表至数据库

    sqlite3 -tabs O2.db 'CREATE TABLE O2 (key PRIMARY KEY, value) WITHOUT ROWID' '.import O2.txt O2'


    4. 逐行查数据库进行翻译

    4.1 为 SQLite 启用 csv 扩展

    ①下载 csv.c: https://www.sqlite.org/src/file?name=ext/misc/csv.c&ci=tip
    ②编译扩展:参考 https://sqlite.org/loadext.html

    4.2 翻译

    SQLITE_CSV_LIB_PATH='./csv' # 编译好的 CSV 模块库路径(可省略后缀)
    SQLITE_CACHE_SIZE_MB=512 # 数据库最大缓存页面大小(单位:MB )

    # sed -E 's/"/""/g; s/^|$/"/g; s/\t/","/g' O1.txt |
    tr '\t' ',' < O1.txt | # 制表符 转成 逗号(要求 O1.txt 每列内容,都不包含『"』『,』,否则用上面那行)

    sqlite3 -tabs O2.db \
      ".load $SQLITE_CSV_LIB_PATH" \
      "PRAGMA cache_size = -$((SQLITE_CACHE_SIZE_MB << 10))" \
      'CREATE VIRTUAL TABLE TEMP.O1 USING csv(filename="/dev/stdin", columns=3)' \
      'SELECT O1.c0 id, O1.c1 expect, O2.value replaced FROM O1 LEFT JOIN O2 ON O1.c2 = O2.key'

    『输出』
    id   expect   replaced
    1    A    A
    2    B    B
    …  …  …
    26    Z    Z
    FYFX
        22
    FYFX  
       2022 年 7 月 23 日
    以前我还会考虑怎么做,现在碰到大点的数据,现在就两张表都塞 hadoop 然后 spark 跑个 join 就好
    winglight2016
        23
    winglight2016  
       2022 年 7 月 23 日
    前面有人提过 dask 、dtable 这些照理够用了,还想通用一点,上 spark 、flink 这种大数据平台
    krixaar
        24
    krixaar  
       2022 年 7 月 23 日
    5GB 左右几亿条,数据条目本身不大而且规整的话,按某种方式(比如先 hash 一下 key )切一下建文件夹和文件,比如 abcdef 对应 123456 就建 ab/cd/ef 路径,文件名 123456 ,建好之后直接 path 判断下有没有,有就把路径里唯一的文件名写进去。
    wxf666
        25
    wxf666  
       2022 年 7 月 23 日
    @krixaar 这个开销有点大噢

    比如 Linux ext4 ,每个文件所需的一个 inode 要 256 字节(存各种属主、权限、时间信息,还有数据分布在哪儿等),

    且不说应该不会预留几亿个 inode 可用,光是建一亿个文件就要 23.8 GB 的 inode ,还没算目录……

    以及长文件名、特殊符号等其他问题

    那还不如用数据库呢,MySQL 的 innodb 下,一行数据仅额外需要至少 18 字节(存事务、回滚信息等),SQLite 更少

    若这个表的 B+树 3~4 层高,前两层容易缓存至内存,那么翻译一行数据一般只需额外读取 1~2 次硬盘,绝对比文件系统开销小
    krixaar
        26
    krixaar  
       2022 年 7 月 23 日
    @wxf666 #25 说了不考虑数据库啊
    另外这个提法是为了黑 Windows 版 QQ 的群图片缓存( Image\Group2 ),腾讯是把文件名开头的四个字符比如 abcdef.jpg 建成 ab/cd 路径然后把 abcdef.jpg 文件塞里面。
    wxf666
        27
    wxf666  
       2022 年 7 月 23 日
    @krixaar 不知为何不考虑数据库,权限不足?不熟悉?

    连 1MB 、无需额外进程 的 SQLite 也要排除。。

    那上面提到的大数据平台就更离谱了


    眼拙,丝毫没看出原来是在讽刺

    其他做得好的类似软件,是如何存储这些小文件的?数据库?

    SQLite 确实提到过,数据库中存储小文件,可比文件系统快 35%,减少 20% 磁盘占用

    https://sqlite.org/fasterthanfs.html
    documentzhangx66
        28
    documentzhangx66  
       2022 年 7 月 23 日   1
    这种场景,你说你不用数据库,我猜测,你应该是 C++ 内嵌汇编的大佬,自己能写出比数据库引擎更好的分析、算法、索引,结果,你起手式却是 Python ?????

    你用 Python 处理数据,不用数据库,反向做方案,是贵司钱多?还是工作量不饱和,要给自己加任务?

    其次,你这最大的数据量,几亿条,才 5GB ,另一个几百万条的文件,估计才几十 MB 吧?不说内存,一些大缓存的洋垃圾 CPU ,说不定都能把你这几百万条文件给一口吃掉。

    不用数据库,这道题就是如何使用 C++,选取极致的字符串匹配算法,实现数据切分到每个核 与 内存,使其处理带宽最大化。

    用数据库,这道题就是怎么安装、配置数据库,进行性能调优。

    这两道题的难度完全不是一个等级的,不是大佬当然选择数据库,最好是地球上最强数据库 Oracle 。

    至于 Python ?如果你的目标是,追求极致的处理性能,那么 Python 在这个问题上,就是个笑话。有些数据库引擎内部能优化到把数据量按 node 与 内存条的对应关系给负载均衡 + 并行起来,Python 却还在研究代码是不是写错了,怎么按行读取这种事情上,画风都不一样。

    当然了,如果只是想叼根烟,翘着二郎腿,在空调房里,摸摸鱼,顺便在上百万的全闪存储 + 王思聪级的服务器上,用 Python 跑个数据玩玩,那当我没说。
    edk24
        29
    edk24  
       2022 年 7 月 24 日
    fopen, 控制游标, 一次读入 n 行, 以此类推 只到遇到文件结尾符


    大文件处理大多是这种套路, windows 的很多设计也是, 因为不可能几个 gb 的文件全部读到内存里面处理.
    mikewang
        30
    mikewang  
       2022 年 7 月 24 日
    ```
    import mmap
    ```
    ?
    zlstone
        31
    zlstone  
       2022 年 7 月 24 日
    估计是个面试题,不让用数据库,只能用 Python 解决
    ranxi
        32
    ranxi  
    OP
       2022 年 7 月 24 日
    抱歉各位,昨天有事没看回复,我再说明一下。

    首先我之前是写 java 的,临时过来写 python ,所以很多用法不了解。
    然后 O1 文件其实不是一个规则的 txt ,而是需要解析的 xml 文件,我是用 sax 逐行读取的。这点之前没有详细说明
    O2 文件跟之前描述一样,是个表形式的 txt 文件,通过第一列映射第二、三列
        33
    wxf666  
       2022 年 7 月 24 日
    @ranxi 描述改个不停。。不如放几行数据出来

    我还是觉得 sqlite 足够你用,几行的事

    下载个 sqlite3.exe ( 1~2MB )就能用,也不用管理员权限,内存占用也随你设,1MB ,1GB ,都行

    xml 也很好解决,xmlstarlet
    crayygy
        34
    crayygy  
       2022 年 7 月 25 日 via iPhone
    @krixaar Git 其实也是这种方式
    ranxi
        35
    ranxi  
    OP
       2022 年 7 月 25 日
    @wxf666 感谢,已解决
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2667 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 03:16 PVG 11:16 LAX 19:16 JFK 22:16
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86