Teilen Sie die Textdatei über die Befehlszeile in eine kleinere Mehrfachtextdatei auf


77

Ich habe mehrere Textdateien mit ungefähr 100.000 Zeilen und möchte sie in kleinere Textdateien mit jeweils 5000 Zeilen aufteilen.

Ich benutzte:

split -l 5000 filename.txt

Das schafft Dateien:

xaa
xab
aac
xad
xbe
aaf

Dateien ohne Erweiterungen. Ich möchte sie nur so nennen:

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

oder wenn das nicht möglich ist, möchte ich nur, dass sie die Erweiterung ".txt" haben.


2
Auf welcher Plattform bist du? Sie sprechen über split(ein Unix / Linux-Dienstprogramm), aber ein Tag, mit batch-filedem Windows ist.
Mark Setchell

1
Mark, ich bin unter Windows, habe aber die Cygwin Bash Shell installiert, sodass ich Zugriff auf split / csplit habe.
ashleybee97

@ MarkSetchell Mark, ja, das tue ich.
ashleybee97

Ashleybee97, haben Sie eine Antwort gefunden
Deepak Jangir

1
Diese Antwort mit PowerShell kann in eine Batchdatei eingebettet werden. Sehen Sie dies als Grundlage.
sancho.s ReinstateMonicaCellio

Antworten:


92

Ich weiß, dass die Frage vor langer Zeit gestellt wurde, aber ich bin überrascht, dass niemand die einfachste Unix-Antwort gegeben hat:

split -l 5000 -d --additional-suffix=.txt $FileName file
  • -l 5000: Datei in Dateien mit jeweils 5.000 Zeilen aufteilen.
  • -d: numerisches Suffix. Dadurch wird das Suffix standardmäßig von 00 auf 99 anstatt von aa auf zz verschoben.
  • --additional-suffix: Hier können Sie das Suffix angeben, hier die Erweiterung
  • $FileName: Name der zu teilenden Datei.
  • file: Präfix zum Hinzufügen zu den resultierenden Dateien.

man splitWeitere Informationen finden Sie wie immer .

Für Mac ist die Standardversion von splitanscheinend heruntergekommen. Sie können die GNU-Version mit dem folgenden Befehl installieren. ( Weitere GNU-Utils finden Sie in dieser Frage. )

brew install coreutils

und dann können Sie den obigen Befehl ausgeführt durch Ersetzen splitmit gsplit. Überprüfen Sie man gsplitfür Details.


2
Wenn ich +100 könnte, würde ich! Mit der von Ihnen veröffentlichten Syntax konnte ich eine> 380M-Datei in ungefähr 0,3 Sekunden in 10M-Dateien aufteilen.
Bakoyaro

1
Es scheint -dund --additional-suffixwerden keine Optionen mehr unterstützt (OSX 10.12.6)
Stefano Munarini

3
@StefanoMunarini für Mac, können Sie die GNU - Version von Split mit installieren brew install coreutils, und dann würden Sie ersetzen splitmit gsplitin dem obigen Befehl.
Ursan

und wie würden Sie ein Delimeter anstelle der Anzahl der Zeilen verwenden?
AGrush

@AGrush Ich bin mir nicht sicher, was genau Ihr Anwendungsfall ist, aber ich denke, Sie könnten das -tFlag verwenden, das auf einem benutzerdefinierten Trennzeichen anstelle eines Zeilenumbruchs aufgeteilt wird. Anschließend können Sie mit dem -lFlag angeben, wie viele Teilungen in der Ausgabedatei zusammengefasst werden sollen.
Ursan

22

Hier ist ein Beispiel in C # (weil ich danach gesucht habe). Ich musste eine 23-GB-CSV-Datei mit etwa 175 Millionen Zeilen teilen, um die Dateien anzeigen zu können. Ich habe es in Dateien mit jeweils einer Million Zeilen aufgeteilt. Dieser Code hat es in ungefähr 5 Minuten auf meinem Computer geschafft:

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
Und Sie können es einfach in LINQPad werfen und nach Herzenslust tweeken. Sie müssen nichts kompilieren. Gute Lösung.
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

Hier ist ein nativer Windows-Stapel, der die Aufgabe erfüllen soll.

Jetzt werde ich nicht sagen, dass es schnell sein wird (weniger als 2 Minuten für jede 5Kline-Ausgabedatei) oder dass es immun gegen Batch-Zeichen-Sensitivitäten ist. Hängt wirklich von den Eigenschaften Ihrer Zieldaten ab.

Ich habe q25249516.txtfür meine Tests eine Datei mit dem Namen 100Klines Daten verwendet.


Überarbeitete schnellere Version

REM

@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

Beachten Sie, dass ich llimit50000 zum Testen verwendet habe. Wird die frühen Dateinummern überschreiben , wenn llimit* 100 gearter als die Anzahl der Zeilen in der Datei ist (Heilung durch Einstellung fcountzu 1999und die Verwendung ~3anstelle von ~2in Datei-Umbenennung Zeile.)


1 MB dauert 5 Minuten zu lange
shareef

@shareef: Die benötigte Zeit sollte von der Anzahl der Zeilen in der Datei abhängen, nicht von der Dateigröße. Ich bin mir nicht sicher, ob Sie 1 MB oder 1 MB Leitungen meinen. Mein Test mit der neuesten Version war 1 Million Zeilen und 11 MB lang.
Magoo

Dies ist gut, aber am Ende jeder Zeile bleibt eine leere Zeile. Wie auch immer, um das zu verhindern?
Arya

@arya: Ich verstehe nicht "eine leere Zeile am Ende jeder Zeile". Die Zeilenenden sind Windows-Standard-CRLF. Die Ausgabe enthält keine Leerzeilen. Vielleicht verwenden Sie ein Dienstprogramm, das sowohl CR als auch LF als neue Zeilen zählt?
Magoo

8

Sie können vielleicht so etwas mit tun awk

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

Grundsätzlich berechnet es den Namen der Ausgabedatei, indem es die Datensatznummer (NR) nimmt und durch 5000 dividiert, 1 addiert, die Ganzzahl davon und das Null-Auffüllen auf 2 Stellen nimmt.

Standardmäßig wird awkder gesamte Eingabedatensatz gedruckt, wenn Sie nichts anderes angeben. So print > outfileschreibt den gesamten Eingabedatensatz in die Ausgabedatei.

Unter Windows können Sie keine einfachen Anführungszeichen verwenden, da dies nicht gefällt. Ich denke, Sie müssen das Skript in eine Datei einfügen und dann anweisen awk, die Datei zu verwenden, ungefähr so:

awk -f script.awk yourfile

und script.awkwird das Skript wie folgt enthalten:

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

Oder es kann funktionieren, wenn Sie dies tun:

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

2
Dadurch ist die erste Datei eine Zeile weniger als die anderen. Die richtige Formel lautet(NR-1)/5000+1
David Balažic

7

Die Syntax sieht folgendermaßen aus:

$ split [OPTION] [INPUT [PREFIX]] 

wobei das Präfix PREFIXaa, PREFIXab, ...

Verwenden Sie einfach die richtige und fertig oder verwenden Sie mv zum Umbenennen. Ich denke, $ mv * *.txt sollte funktionieren, aber testen Sie es zuerst in kleinerem Maßstab.

:) :)


5

Meine Anforderung war etwas anders. Ich arbeite oft mit durch Kommas getrennten und durch Tabulatoren getrennten ASCII-Dateien, bei denen eine einzelne Zeile ein einzelner Datensatz von Daten ist. Und sie sind wirklich groß, also muss ich sie in überschaubare Teile aufteilen (unter Beibehaltung der Kopfzeile).

Also kehrte ich zu meiner klassischen VBScript-Methode zurück und schlug ein kleines .vbs-Skript zusammen, das auf jedem Windows-Computer ausgeführt werden kann (es wird automatisch von der Skript-Host-Engine WScript.exe unter Windows ausgeführt).

Der Vorteil dieser Methode besteht darin, dass Textströme verwendet werden, sodass die zugrunde liegenden Daten nicht (oder zumindest nicht alle gleichzeitig) in den Speicher geladen werden. Das Ergebnis ist, dass es außergewöhnlich schnell ist und nicht wirklich viel Speicher benötigt, um ausgeführt zu werden. Die Testdatei, die ich gerade mit diesem Skript auf meinem i7 geteilt habe, hatte eine Dateigröße von ungefähr 1 GB, hatte ungefähr 12 Millionen Testzeilen und erstellte 25 Teiledateien (jede mit jeweils ungefähr 500.000 Zeilen) - die Verarbeitung dauerte ungefähr 2 Minuten und es dauerte nicht Gehen Sie zu keinem Zeitpunkt über 3 MB Speicher.

Die Einschränkung hierbei ist, dass die Textdatei "Zeilen" enthält (dh jeder Datensatz wird durch eine CRLF begrenzt), da das Textstromobjekt die Funktion "ReadLine" verwendet, um jeweils eine einzelne Zeile zu verarbeiten. Aber hey, wenn Sie mit TSV- oder CSV-Dateien arbeiten, ist es perfekt.

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

Hier ist eine in c #, die beim Aufteilen in große Teile nicht über genügend Speicher verfügt! Ich musste 95 Millionen Dateien in 10 Millionen x Zeilendateien aufteilen.

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

Ich habe ein einfaches Programm dafür erstellt und Ihre Frage hat mir geholfen, die Lösung zu vervollständigen ... Ich habe eine weitere Funktion und einige Konfigurationen hinzugefügt. Falls Sie nach jeweils wenigen Zeilen ein bestimmtes Zeichen / eine bestimmte Zeichenfolge hinzufügen möchten (konfigurierbar). Bitte gehen Sie die Notizen durch. Ich habe die Codedateien hinzugefügt: https://github.com/mohitsharma779/FileSplit

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.