【问题标题】:Split text file into smaller multiple text file using command line使用命令行将文本文件拆分为更小的多个文本文件
【发布时间】:2014-10-04 15:35:39
【问题描述】:

我有多个大约 100,000 行的文本文件,我想将它们拆分为每个 5000 行的较小文本文件。

我用过:

split -l 5000 filename.txt

创建文件:

xaa
xab
aac
xad
xbe
aaf

没有扩展名的文件。我只想称它们为:

file01.txt
file02.txt
file03.txt
file04.txt

或者,如果这不可能,我只希望它们具有“.txt”扩展名。

【问题讨论】:

  • 你在什么平台上?您谈论的是split(一个Unix/Linux 实用程序),但标记为batch-file,即Windows。
  • 马克,我在 Windows 上,但安装了 Cygwin bash shell,所以我可以访问 split/csplit。
  • @MarkSetchell Mark,是的,我愿意。
  • Ashleybee97,你找到答案了吗
  • This answer 可以嵌入到批处理文件中。请参阅this 了解基础。

标签: batch-file split command cygwin text-files


【解决方案1】:

我知道这个问题很久以前就被问过了,但我很惊讶没有人给出最直接的 unix 答案:

split -l 5000 -d --additional-suffix=.txt $FileName file
  • -l 5000:将文件拆分为每个 5000 行的文件。
  • -d:数字后缀。这将使后缀默认从 00 变为 99,而不是从 aa 变为 zz。
  • --additional-suffix:让你指定后缀,这里是扩展名
  • $FileName:要拆分的文件名。
  • file:添加到结果文件的前缀。

与往常一样,请查看man split 了解更多详情。

对于 Mac,split 的默认版本显然被简化了。您可以使用以下命令安装 GNU 版本。 (see this question for more GNU utils)

brew install coreutils

然后您可以通过将split 替换为gsplit 来运行上述命令。详情请查看man gsplit

【讨论】:

  • 如果我能 +100 我会的!使用您发布的语法,我能够在大约 0.3 秒内将 >380M 的文件拆分为 10M 的文件。
  • 似乎 -d--additional-suffix 不再受支持的选项(OSX 10.12.6)
  • @StefanoMunarini for mac,你可以用brew install coreutils安装gnu版本的split,然后在上面的命令中用gsplit替换split
  • 你将如何使用分隔符而不是行数?
  • @AGrush 我不确定您的用例到底是什么,但我认为您可以使用-t 标志,它在用户指定的分隔符而不是换行符上分割。然后,您可以使用 -l 标志指定要在输出文件中组合多少个拆分。
【解决方案2】:

我为此创建了一个简单的程序,您的问题帮助我完成了解决方案... 我增加了一项功能和一些配置。 如果您想在每几行之后添加一个特定的字符/字符串(可配置)。请仔细阅读笔记。 我添加了代码文件: https://github.com/mohitsharma779/FileSplit

【讨论】:

    【解决方案3】:

    这是 C# 中的一个,在拆分成大块时不会耗尽内存!我需要将 95M 文件拆分为 10M x 行文件。

    var fileSuffix = 0;
    int lines = 0;
    Stream fstream = File.OpenWrite($"{filename}.{(++fileSuffix)}");
    StreamWriter sw = new StreamWriter(fstream);
    
    using (var file = File.OpenRead(filename))
    using (var reader = new StreamReader(file))
    {
        while (!reader.EndOfStream)
        {
            sw.WriteLine(reader.ReadLine());
            lines++;
    
            if (lines >= 10000000)
            {
                  sw.Close();
                  fstream.Close();
                  lines = 0;
                  fstream = File.OpenWrite($"{filename}.{(++fileSuffix)}");
                  sw = new StreamWriter(fstream);
            }
        }
    }
    
    sw.Close();
    fstream.Close();
    

    【讨论】:

      【解决方案4】:

      这个“文件拆分器”Windows 命令行程序运行良好:https://github.com/dubasdey/File-Splitter

      它是开源的、简单的、有文档的、经过验证的,并且对我有用。

      例子:

      fsplit -split 50 mb mylargefile.txt
      

      【讨论】:

        【解决方案5】:

        这是 C# 中的一个示例(因为这就是我要搜索的内容)。我需要拆分一个包含大约 1.75 亿行的 23 GB csv 文件才能查看这些文件。我把它分成一百万行的文件。这段代码在我的机器上用了大约 5 分钟:

        var list = new List<string>();
        var fileSuffix = 0;
        
        using (var file = File.OpenRead(@"D:\Temp\file.csv"))
        using (var reader = new StreamReader(file))
        {
            while (!reader.EndOfStream)
            {
                list.Add(reader.ReadLine());
        
                if (list.Count >= 1000000)
                {
                    File.WriteAllLines(@"D:\Temp\split" + (++fileSuffix) + ".csv", list);
                    list = new List<string>();
                }
            }
        }
        
        File.WriteAllLines(@"D:\Temp\split" + (++fileSuffix) + ".csv", list);
        

        【讨论】:

        • 你基本上可以把它扔到 LINQPad 中,然后就可以尽情享受了。无需编译任何东西。很好的解决方案。
        【解决方案6】:
        @ECHO OFF
        SETLOCAL
        SET "sourcedir=U:\sourcedir"
        SET /a fcount=100
        SET /a llimit=5000
        SET /a lcount=%llimit%
        FOR /f "usebackqdelims=" %%a IN ("%sourcedir%\q25249516.txt") DO (
         CALL :select
         FOR /f "tokens=1*delims==" %%b IN ('set dfile') DO IF /i "%%b"=="dfile" >>"%%c" ECHO(%%a
        )
        GOTO :EOF
        :select
        SET /a lcount+=1
        IF %lcount% lss %llimit% GOTO :EOF
        SET /a lcount=0
        SET /a fcount+=1
        SET "dfile=%sourcedir%\file%fcount:~-2%.txt"
        GOTO :EOF
        

        这是应该完成任务的本机 Windows 批处理。

        现在我不会说它会很快(每个 5Kline 输出文件不到 2 分钟)或者它不会受到批处理字符敏感的影响。真的取决于你的目标数据的特征。

        我使用了一个名为 q25249516.txt 的文件,其中包含 100Klines 的数据用于我的测试。


        修订更快的版本

        快速移动

        @ECHO OFF
        SETLOCAL
        SET "sourcedir=U:\sourcedir"
        SET /a fcount=199
        SET /a llimit=5000
        SET /a lcount=%llimit%
        FOR /f "usebackqdelims=" %%a IN ("%sourcedir%\q25249516.txt") DO (
         CALL :select
         >>"%sourcedir%\file$$.txt" ECHO(%%a
        )
        SET /a lcount=%llimit%
        :select
        SET /a lcount+=1
        IF %lcount% lss %llimit% GOTO :EOF
        SET /a lcount=0
        SET /a fcount+=1
        MOVE /y "%sourcedir%\file$$.txt" "%sourcedir%\file%fcount:~-2%.txt" >NUL 2>nul
        GOTO :EOF
        

        请注意,我使用了 50000 的 llimit 进行测试。如果llimit*100 大于文件中的行数,将覆盖早期文件编号(通过将fcount 设置为1999 并在文件重命名行中使用~3 代替~2 来解决。 )

        【讨论】:

        • 1 MB 需要 5 分钟太长
        • @shareef:所花费的时间应该取决于文件中的行数,而不是文件大小。不确定您是指 1Mb 还是 1M 行。我对最新版本的测试是 1M 行和 11Mb 长。
        • 这很好,但它会在每一行的末尾留下一个空白行。无论如何要防止这种情况发生?
        • @arya :我不明白“每行末尾有一个空行”。行尾是 Windows 标准的 CRLF。输出中没有空行。也许您正在使用将 CR 和 LF 都视为换行符的实用程序?
        【解决方案7】:

        语法如下:

        $ split [OPTION] [INPUT [PREFIX]] 
        

        前缀在哪里 PREFIXaa, PREFIXab, ...

        只要使用正确的就可以了,或者只是使用 mv 来重命名。 我想 $ mv * *.txt 应该可以,但首先要在较小的范围内进行测试。

        :)

        【讨论】:

          【解决方案8】:

          我的要求有点不同。我经常使用逗号分隔和制表符分隔的 ASCII 文件,其中单行是单条数据记录。而且它们真的很大,所以我需要将它们分成可管理的部分(同时保留标题行)。

          所以,我恢复了我的经典 VBScript 方法,并拼凑了一个可以在任何 Windows 计算机上运行的小型 .vbs 脚本(它由 Window 上的 WScript.exe 脚本主机引擎自动执行)。

          这种方法的好处是它使用文本流,因此不会将底层数据加载到内存中(或者至少不是一次全部加载)。结果是它非常快,并且运行起来并不需要太多内存。我刚刚在 i7 上使用此脚本拆分的测试文件的文件大小约为 1 GB,有大约 1200 万行测试并制作了 25 个部分文件(每个文件大约有 500k 行)——处理大约需要 2 分钟,但它没有'在任何时候都不会超过 3 MB 内存。

          这里需要注意的是,它依赖于具有“行”的文本文件(意味着每条记录用 CRLF 分隔),因为文本流对象使用“ReadLine”函数一次处理一行。但是,嘿,如果您使用的是 TSV 或 CSV 文件,那就完美了。

          Option Explicit
          
          Private Const INPUT_TEXT_FILE = "c:\bigtextfile.txt"  'The full path to the big file
          Private Const REPEAT_HEADER_ROW = True                'Set to True to duplicate the header row in each part file
          Private Const LINES_PER_PART = 500000                 'The number of lines per part file
          
          Dim oFileSystem, oInputFile, oOutputFile, iOutputFile, iLineCounter, sHeaderLine, sLine, sFileExt, sStart
          
          sStart = Now()
          
          sFileExt = Right(INPUT_TEXT_FILE,Len(INPUT_TEXT_FILE)-InstrRev(INPUT_TEXT_FILE,".")+1)
          iLineCounter = 0
          iOutputFile = 1
          
          Set oFileSystem = CreateObject("Scripting.FileSystemObject")
          Set oInputFile = oFileSystem.OpenTextFile(INPUT_TEXT_FILE, 1, False)
          Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)
          
          If REPEAT_HEADER_ROW Then
              iLineCounter = 1
              sHeaderLine = oInputFile.ReadLine()
              Call oOutputFile.WriteLine(sHeaderLine)
          End If
          
          Do While Not oInputFile.AtEndOfStream
              sLine = oInputFile.ReadLine()
              Call oOutputFile.WriteLine(sLine)
              iLineCounter = iLineCounter + 1
              If iLineCounter Mod LINES_PER_PART = 0 Then
                  iOutputFile = iOutputFile + 1
                  Call oOutputFile.Close()
                  Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)
                  If REPEAT_HEADER_ROW Then
                      Call oOutputFile.WriteLine(sHeaderLine)
                  End If
              End If
          Loop
          
          Call oInputFile.Close()
          Call oOutputFile.Close()
          Set oFileSystem = Nothing
          
          Call MsgBox("Done" & vbCrLf & "Lines Processed:" & iLineCounter & vbCrLf & "Part Files: " & iOutputFile & vbCrLf & "Start Time: " & sStart & vbCrLf & "Finish Time: " & Now())
          

          【讨论】:

            【解决方案9】:

            你也许可以用awk做这样的事情

            awk '{outfile=sprintf("file%02d.txt",NR/5000+1);print > outfile}' yourfile
            

            基本上,它通过将记录号 (NR) 除以 5000、加 1、取其整数和补零到 2 位来计算输出文件的名称。

            默认情况下,awk 在您未指定任何其他内容时打印整个输入记录。因此,print &gt; outfile 将整个输入记录写入输出文件。

            当您在 Windows 上运行时,不能使用单引号,因为它不喜欢这样。我认为您必须将脚本放入文件中,然后告诉awk使用该文件,如下所示:

            awk -f script.awk yourfile
            

            script.awk 将包含这样的脚本:

            {outfile=sprintf("file%02d.txt",NR/5000+1);print > outfile}
            

            或者,如果你这样做,它可能会起作用:

            awk "{outfile=sprintf(\"file%02d.txt\",NR/5000+1);print > outfile}" yourfile
            

            【讨论】:

            • 这使得第一个文件比其他文件少一行。正确的公式是(NR-1)/5000+1
            猜你喜欢
            • 2013-04-22
            • 2018-06-02
            • 1970-01-01
            • 2016-06-25
            • 1970-01-01
            • 2014-06-09
            • 1970-01-01
            • 2014-05-23
            • 1970-01-01
            相关资源
            最近更新 更多