V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Nick2VIPUser
V2EX  ›  程序员

请教高效分割 txt 大文件(100GB)方案( Python , Java , shell)

  •  
  •   Nick2VIPUser ·
    nickliqian · 2020-04-09 18:16:29 +08:00 · 6620 次点击
    这是一个创建于 1680 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一个 100G 的 txt 文件,按指定行内容分割为若干个文件,如何处理?
    例如:这个文件有几亿行,其中会有大概 20 行的内容是”my content\t“
    想以这个内容将文件分割开,如何在内存 16GB 的电脑上实现呢?

    40 条回复    2020-04-23 09:46:33 +08:00
    xupefei
        1
    xupefei  
       2020-04-09 18:18:31 +08:00 via iPhone   ❤️ 2
    边读边写,线性时间,内存占用超低。
    littleylv
        2
    littleylv  
       2020-04-09 18:21:11 +08:00   ❤️ 1
    歪楼,为啥会有这样的文件啊,可怕
    alan0liang
        3
    alan0liang  
       2020-04-09 18:21:15 +08:00 via Android   ❤️ 1
    xupefei
        4
    xupefei  
       2020-04-09 18:22:11 +08:00 via iPhone
    如果是 ssd 的话,可以把文件分块后多线程扫描来拿到分隔符位置。对于每个位置,起一个线程从那个位置开始边读边写,直到遇到下一个分隔符。
    vuuv
        5
    vuuv  
       2020-04-09 18:22:23 +08:00 via Android   ❤️ 1
    根据内容分割需要用 csplit 。
    alan0liang
        6
    alan0liang  
       2020-04-09 18:23:05 +08:00 via Android
    哦看错了 一楼流处理正解
    luckyrayyy
        7
    luckyrayyy  
       2020-04-09 18:28:13 +08:00
    你的难点在哪?如果说只是不能一次性读到内存,那就一边读一边处理呗。是否还有其他困难?
    vuuv
        8
    vuuv  
       2020-04-09 18:32:15 +08:00 via Android
    毕竟只会生成 20 多个文件,都是顺序大块读写。
    如果可能,写的目的地不要放在同一块磁盘上。
    复制到 SSD 后能加快处理时的读取速度。但是你复制也要时间。自行决策吧。

    提前创建个小文件测试下命令行参数效果。
    Nick2VIPUser
        9
    Nick2VIPUser  
    OP
       2020-04-09 18:42:35 +08:00
    @xupefei 是的~我的想法是读 2GB,然后逐行写入,依次循环。我打算先计算预计需要的时间...如果是读一行写一行,算了一下时间不能承受。
    Nick2VIPUser
        10
    Nick2VIPUser  
    OP
       2020-04-09 18:43:23 +08:00
    @littleylv 因为自己的某些 zhizhang 操作...哭了
    Nick2VIPUser
        11
    Nick2VIPUser  
    OP
       2020-04-09 18:43:46 +08:00
    @vuuv 我回去试试
    yxt
        12
    yxt  
       2020-04-09 18:45:21 +08:00 via Android
    emeditor?
    Nick2VIPUser
        13
    Nick2VIPUser  
    OP
       2020-04-09 18:45:53 +08:00
    @luckyrayyy 目前准备是按 2GB 分批读,然后逐行判断和写入,目前还在找 python 相关的 api 进行实现
    Nick2VIPUser
        14
    Nick2VIPUser  
    OP
       2020-04-09 18:46:41 +08:00
    @vuuv 是了~我打算放在 ssd 上跑一下
    pcbl
        15
    pcbl  
       2020-04-09 18:50:53 +08:00 via Android
    反正才 100g 如果只是一次性处理的话,一行一行的读取都行,然后预估下内存最大可以放多少行在超出之前写入到文件
    yxt
        16
    yxt  
       2020-04-09 18:50:53 +08:00 via Android
    emeditor 最大支持大概 250g 先搜索,bookmark all,然后 split done
    aptupdate
        17
    aptupdate  
       2020-04-09 18:52:11 +08:00 via iPhone
    在 SSD 里面把线程拉满然后边读边写,你是想分割成 20 个文件吗?感觉分割完 20 个 5GB 大小的文件后续处理也够呛。好奇 100GB 的 txt 文件是什么东西……
    also24
        18
    also24  
       2020-04-09 18:53:40 +08:00
    提醒下,边读边写方案,如果你是 HDD,最好分开在两个硬盘进行,或者读取一长段数据( x GB )后再一次性写入,避免大量的随机读写降低 IO 性能。

    如果是 SSD 的话可以不用管这个。
    caola
        19
    caola  
       2020-04-09 18:55:43 +08:00
    逐行读,判后再写入新文件……
    line
        20
    line  
       2020-04-09 20:34:35 +08:00
    cat | awk
    rayray314
        21
    rayray314  
       2020-04-09 21:07:03 +08:00 via Android
    emeditor
    cheng6563
        22
    cheng6563  
       2020-04-09 21:47:00 +08:00
    就算是 HDD 同盘复制缓冲区给个 50M 就行了,没必要上 GB
    lululau
        23
    lululau  
       2020-04-09 22:20:20 +08:00
    zsh:

    offsets=($(echo 1; grep -b 'my content' big.log | cut -d: -f1; stat -c%s big.log))
    for ((i=1; i < $#offsets; i++)) { echo dd if=big.log of=small.$i.log bs=1 count=$[$offsets[i+1]-$offsets[i]] skip=$[$offsets[i]-1] }
    lululau
        24
    lululau  
       2020-04-09 22:20:49 +08:00   ❤️ 1
    zsh:

    offsets=($(echo 1; grep -b 'my content' big.log | cut -d: -f1; stat -c%s big.log))
    for ((i=1; i < $#offsets; i++)) { dd if=big.log of=small.$i.log bs=1 count=$[$offsets[i+1]-$offsets[i]] skip=$[$offsets[i]-1] }
    lniwn
        25
    lniwn  
       2020-04-09 23:32:28 +08:00   ❤️ 1
    mmap 内存映射,一个映射读的文件,一个映射写的文件,64mb 一个块,一个块 commit 一次,hdd 没有任何压力。
    qinrui
        26
    qinrui  
       2020-04-09 23:51:57 +08:00 via iPhone
    awk 一行一行的读
    scriptB0y
        27
    scriptB0y  
       2020-04-09 23:58:03 +08:00
    直接用 fileinput,无论按照什么逻辑分割都能三五行搞定

    https://docs.python.org/3.8/library/fileinput.html
    rrfeng
        28
    rrfeng  
       2020-04-10 00:13:57 +08:00 via Android
    sed
    awk
    都能轻易完成…
    popbones
        29
    popbones  
       2020-04-10 00:27:12 +08:00
    msg7086
        30
    msg7086  
       2020-04-10 00:35:15 +08:00
    读进 buffer 然后扫描分割就行了吧。
    一次读比如说 128M,扫描完,再读下一块,如果能多线程跑更好,IO 扔给子线程。
    marcomarco
        31
    marcomarco  
       2020-04-10 07:33:56 +08:00 via iPhone
    txtkiller 了解一下
    sxfscool
        32
    sxfscool  
       2020-04-10 08:44:11 +08:00
    bufio
    dantegg
        33
    dantegg  
       2020-04-10 08:53:40 +08:00
    sed +1
    BlackBerry999
        34
    BlackBerry999  
       2020-04-10 10:01:05 +08:00
    按行读,边读边写。
    matepi
        35
    matepi  
       2020-04-10 10:06:02 +08:00
    边读边写不就完了么

    真正难的是让你快速定位到随机第 n 行的内容
    zz554952942
        36
    zz554952942  
       2020-04-10 10:35:14 +08:00
    go
    开一个生产者协程 负责读取文本 读到换行符则塞到通道上
    然后开一定量的消费者协程负责取然后写入
    llussy
        37
    llussy  
       2020-04-10 11:15:05 +08:00
    split -b 10G log.txt newfile
    feelinglucky
        38
    feelinglucky  
       2020-04-10 12:18:30 +08:00
    @littleylv 以前很多日志都还存文件系统的时候,一天就要好几百 G 了…🤣
    augustheart
        39
    augustheart  
       2020-04-10 14:07:18 +08:00
    逐字节线性读入就行。你只需要长度为“my content\t”的缓存就行
    JimiJimi
        40
    JimiJimi  
       2020-04-23 09:46:33 +08:00
    流处理,一行一行读
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5525 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 08:48 · PVG 16:48 · LAX 00:48 · JFK 03:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.