Tekstbestand splitsen in kleinere meervoudige tekstbestanden met behulp van de opdrachtregel

Ik heb meerdere tekstbestanden met ongeveer 100.000 regels en ik wil ze opsplitsen in kleinere tekstbestanden van elk 5000 regels.

Ik gebruikte:

split -l 5000 filename.txt

Dat creëert bestanden:

xaa
xab
aac
xad
xbe
aaf

bestanden zonder extensies. Ik wil ze gewoon zo noemen:

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

of als dat niet mogelijk is, wil ik dat ze de extensie “.txt” hebben.


Antwoord 1, autoriteit 100%

Ik weet dat de vraag al lang geleden is gesteld, maar het verbaast me dat niemand het meest duidelijke unix-antwoord heeft gegeven:

split -l 5000 -d --additional-suffix=.txt $FileName file
  • -l 5000: splits het bestand op in bestanden van elk 5.000 regels.
  • -d: numeriek achtervoegsel. Hierdoor gaat het achtervoegsel standaard van 00 naar 99 in plaats van aa naar zz.
  • --additional-suffix: laat je het achtervoegsel specificeren, hier de extensie
  • $FileName: naam van het te splitsen bestand.
  • file: prefix om toe te voegen aan de resulterende bestanden.

Bekijk zoals altijd man splitvoor meer details.

Voor Mac is de standaardversie van splitblijkbaar afgezwakt. U kunt de GNU-versie installeren met de volgende opdracht. (bekijk deze vraag voor meer GNU-hulpprogramma’s )

brew install coreutils

en dan kun je het bovenstaande commando uitvoeren door splitte vervangen door gsplit. Bekijk man gsplitvoor details.


Antwoord 2, autoriteit 20%

Hier is een voorbeeld in C # (want dat is waar ik naar op zoek was). Ik moest een 23 GB CSV-bestand opsplitsen met ongeveer 175 miljoen lijnen om naar de bestanden te kunnen kijken. Ik splits het in bestanden van een miljoen rijen elk. Deze code deed het in ongeveer 5 minuten op mijn machine:

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);

Antwoord 3, Autoriteit 14%

@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 is een inheemse Windows-batch die de taak moet bereiken.

Nu zal ik niet zeggen dat het snel zal zijn (minder dan 2 minuten voor elk 5Kline-uitvoerbestand) of dat het immuun is voor batch-tekens-gevoeligheden. Hangt echt af van de kenmerken van uw doelgegevens.

Ik heb een bestand gebruikt met de naam q25249516.txtmet 100Klines van gegevens voor mijn tests.


Herziene snellere versie

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

Merk op dat ik llimitvan 50000 heb gebruikt om te testen. Zal de vroege bestandsnummers overschrijven als llimit*100 sneller is dan het aantal regels in het bestand (genezen door fcountin te stellen op 1999en te gebruiken ~3in plaats van ~2in de regel voor het hernoemen van bestanden.)


Antwoord 4, autoriteit 7%

Je kunt misschien zoiets doen met awk

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

Kortom, het berekent de naam van het uitvoerbestand door het recordnummer (NR) te nemen en dit te delen door 5000, 1 op te tellen, het gehele getal daarvan te nemen en nul-opvulling op 2 plaatsen.

Standaard drukt awkhet volledige invoerrecord af als u niets anders opgeeft. Dus, print > outfileschrijft het volledige invoerbestand naar het uitvoerbestand.

Omdat u op Windows werkt, kunt u geen enkele aanhalingstekens gebruiken, omdat dat niet prettig is. Ik denk dat je het script in een bestand moet plaatsen en dan awkmoet vertellen om het bestand te gebruiken, zoiets als dit:

awk -f script.awk yourfile

en script.awkzullen het script als volgt bevatten:

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

Of het kan werken als je dit doet:

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

Antwoord 5, Autoriteit 6%

Syntaxis ziet eruit als:

$ split [OPTION] [INPUT [PREFIX]] 

waar voorvoegsel is
Prefixaa, Prefixab, …

Gebruik gewoon de juiste en u hebt gedaan of gebruik gewoon MV voor het hernoemen.
I denk
$ mv * *.txt
zou moeten werken, maar het eerst op kleinere schaal testen.

🙂


Antwoord 6, Autoriteit 6%

Deze “File Splitter” Windows Command Line-programma werkt mooi: https://github.com/dubasdey/ Bestand-splitter

Het is open source, eenvoudig, gedocumenteerd, bewezen en werkte voor mij.

Voorbeeld:

fsplit -split 50 mb mylargefile.txt

Antwoord 7, Autoriteit 5%

Mijn vereiste was een beetje anders. Ik werk vaak met door komma’s gescheiden en tabblad gescheiden ASCII-bestanden waar een enkele regel een enkele verslag van gegevens is. En ze zijn erg groot, dus ik moet ze in beheersbare onderdelen splitsen (tijdens het behoud van de header rij).

Dus, keerde ik terug naar mijn klassieke Vbscript-methode en bashed een klein. VBBS-script dat op elke Windows-computer kan worden uitgevoerd (deze wordt automatisch uitgevoerd door de WScript.exe-script-hostmotor op het venster).

Het voordeel van deze methode is dat het tekststromen gebruikt, zodat de onderliggende gegevens niet in het geheugen worden geladen (of, tenminste, niet allemaal tegelijk). Het resultaat is dat het uitzonderlijk snel is en het heeft niet echt veel geheugen nodig om te rennen. Het testbestand dat ik net heb gesplitd met dit script op mijn I7 was ongeveer 1 GB in de bestandsgrootte, had ongeveer 12 miljoen testlijnen en maakte 25 deelbestanden (elk met ongeveer 500K-lijnen elk) – de verwerking duurde ongeveer 2 minuten en het duurde ongeveer 2 minuten ‘t Gaan meer dan 3 MB geheugen dat op elk moment wordt gebruikt.

Het voorbehoud hierbij is dat het afhankelijk is van het tekstbestand met “regels” (wat betekent dat elk record wordt gescheiden door een CRLF) omdat het Text Stream-object de functie “ReadLine” gebruikt om één regel tegelijk te verwerken. Maar goed, als je met TSV- of CSV-bestanden werkt, is het perfect.

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())

Antwoord 8, Autoriteit 2%

Hier is een in C # die niet helemaal geen geheugen heeft bij het splitsen in grote brokken! Ik moest het 95M-bestand opsplitsen in 10M X-lijnbestanden.

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

Antwoord 9

Ik heb hiervoor een eenvoudig programma gemaakt en uw vraag heeft me geholpen de oplossing te voltooien …
Ik heb nog een functie en weinig configuraties toegevoegd.
Als u na elke paar regels een specifiek teken / tekenreeks wilt toevoegen (configureerbaar). Ga alsjeblieft door de aantekeningen.
Ik heb de codebestanden toegevoegd:
https://github.com/mohitsharma779/filesplit

Other episodes