使用命令行将文本文件拆分为较小的多个文本文件


77

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

我用了:

split -l 5000 filename.txt

创建文件:

xaa
xab
aac
xad
xbe
aaf

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

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

或者,如果这不可能,我只希望他们使用“ .txt”扩展名。


2
您在什么平台上?您谈论的是split(Unix / Linux实用程序),但是使用batch-fileWindows标记。
马克·谢彻尔

1
马克,我在Windows上,但安装了Cygwin bash shell,因此我可以使用split / csplit。
ashleybee97 2014年

@MarkSetchell马克,是的。
ashleybee97

Ashleybee97,您没有找到任何答案吗
Deepak Jangir

1
可以将PowerShell的此答案嵌入批处理文件中。请参阅作为基础。
sancho.s ReinstateMonicaCellio

Answers:


92

我知道这个问题已经问了很久了,但是我很惊讶没有人给出最直接的unix答案:

split -l 5000 -d --additional-suffix=.txt $FileName file
  • -l 5000:将文件分成5,000行的文件。
  • -d:数字后缀。这将使后缀默认从00到99,而不是从a到zz。
  • --additional-suffix:让您指定后缀,此处为扩展名
  • $FileName:要分割的文件名。
  • file:添加到结果文件的前缀。

与往常一样,请man split查看更多详细信息。

对于Mac,split显然是默认版本的。您可以使用以下命令安装GNU版本。(有关更多GNU utils,请参见此问题

brew install coreutils

然后您可以将替换split为来运行上述命令gsplit。查看man gsplit详细信息。


2
如果我能+100,我会的!使用您发布的语法,我能够在大约.3秒的时间内将> 380M的文件拆分为10M的文件。
bakoyaro

1
似乎-d并且--additional-suffix不再支持该选项(OSX 10.12.6)
Stefano Munarini

3
@StefanoMunarini for mac,您可以使用安装GNU版本的split brew install coreutils,然后在上面的命令中替换splitgsplit
ursan

以及如何使用分号而不是行数?
AGrush

@AGrush我不确定您的用例是什么,但是我认为您可以使用在-t用户指定的定界符而不是换行符上分割的标志。然后,您可以使用该-l标志来指定要在输出文件中分组的拆分数。
ursan

22

这是C#中的示例(因为这就是我要搜索的内容)。我需要分割一个23 GB的csv文件,其中包含约1.75亿行,以便能够查看这些文件。我将其拆分为每个一百万行的文件。这段代码是在我的计算机上大约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);

2
而且,您基本上可以将其放入LINQPad中,而只需两周时间便可以放心使用。无需编译任何东西。好的解决方案。
Zachary Dow

15
@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.txt100Klines数据的文件进行测试。


修订更快的版本

快速眼动

@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

请注意,我使用llimit了50000进行测试。如果llimit* 100比文件中的行数更重要,则将覆盖早期的文件编号(通过设置fcount1999~3代替~2文件重命名行来进行固化。)


1 MB需要5分钟太长时间
shareef

@shareef:花费的时间应取决于文件中的行数,而不是文件大小。不确定您是指1Mb还是1M的线路。我对最新版本的测试是1M线路和11Mb长。
Magoo

这很好,但是在每一行的末尾留了一个空白行。无论如何要防止这种情况?
艾莉亚

@arya:我不明白“每行末尾有一个空白行”。线尾是Windows标准的CRLF。输出中没有空行。也许您正在使用将CR和LF都视为换行符的实用程序?
Magoo

8

您也许可以用 awk

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

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

默认情况下,awk当您未指定其他任何内容时,将打印整个输入记录。因此,print > 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

2
这使第一个文件比其他文件少一行。正确的公式是(NR-1)/5000+1
DavidBalažic16年

7

语法如下:

$ split [OPTION] [INPUT [PREFIX]] 

其中前缀是PREFIXaa,PREFIXab,...

只需使用正确的代码即可完成操作,或者仅使用mv进行重命名。我认为 $ mv * *.txt 应该可以工作,但先要进行较小规模的测试。

:)


5

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

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

这种方法的好处是它使用文本流,因此不会将基础数据加载到内存中(或者至少不会一次全部加载到内存中)。结果是它运行速度极快,并且实际上不需要太多内存即可运行。我刚刚在i7上使用此脚本分割的测试文件大小约为1 GB,进行了大约1200万行测试,并制作了25个零件文件(每个文件约50万行)–处理过程大约需要2分钟,在任何时候都不会超过3 MB的内存。

需要注意的是,由于文本流对象使用“ ReadLine”功能一次只能处理一行,因此它依赖于具有“行”(意味着每个记录都用CRLF分隔)的文本文件。但是,嘿,如果您使用的是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())


2

这是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();

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.