web-dev-qa-db-fra.com

Fractionner un fichier texte en plusieurs fichiers texte plus petits à l'aide de la ligne de commande

J'ai plusieurs fichiers texte avec environ 100 000 lignes et je veux les séparer en fichiers texte plus petits de 5000 lignes chacun 

J'ai utilisé:

split -l 5000 filename.txt

Cela crée des fichiers:

xaa
xab
aac
xad
xbe
aaf

fichiers sans extensions. Je veux juste les appeler quelque chose comme:

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

ou si ce n'est pas possible, je veux juste qu'ils aient l'extension ".txt". 

60
ashleybee97

Je sais que la question a été posée il y a longtemps, mais je suis surpris que personne n'ait donné la réponse unix la plus simple:

split -l 5000 -d --additional-suffix=.txt $FileName file
  • -l 5000: divise le fichier en fichiers de 5 000 lignes chacun.
  • -d: suffixe numérique. Cela fera passer le suffixe de 00 à 99 par défaut au lieu de aa à zz.
  • --additional-suffix: permet de spécifier le suffixe, ici l'extension
  • $FileName: nom du fichier à scinder.
  • file: préfixe à ajouter aux fichiers résultants.

Comme toujours, consultez man split pour plus de détails.

Pour Mac, la version par défaut de split est apparemment masquée. Vous pouvez installer la version GNU en utilisant la commande suivante. ( voir cette question pour plus de GNU utils )

brew install coreutils

vous pouvez ensuite exécuter la commande ci-dessus en remplaçant split par gsplit. Consultez man gsplit pour plus de détails.

61
ursan

Voici un exemple en C # (car c'est ce que je cherchais). Je devais diviser un fichier csv de 23 Go avec environ 175 millions de lignes pour pouvoir consulter les fichiers. Je l'ai divisé en fichiers d'un million de lignes chacun. Ce code l'a fait en 5 minutes environ sur ma 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);
18
Alex
@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

Voici un lot Windows natif qui devrait accomplir la tâche.

Maintenant, je ne dirai pas que ce sera rapide (moins de 2 minutes pour chaque fichier de sortie 5Kline) ou qu'il sera à l'abri de la sensibilité aux lots. Cela dépend vraiment des caractéristiques de vos données cibles.

J'ai utilisé un fichier nommé q25249516.txt contenant 100Klines de données pour mes tests.


Version révisée plus rapide

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

Notez que j'ai utilisé llimit de 50000 pour les tests. Remplacera les anciens numéros de fichier si llimit * 100 correspond au nombre de lignes du fichier (remuez en définissant fcount sur 1999 et utilisez ~3 à la place de ~2 dans la ligne de changement de nom de fichier.)

15
Magoo

La syntaxe ressemble à:

$ split [OPTION] [INPUT [PREFIX]] 

où préfixe est PREFIXaa, PREFIXab, ... 

Il suffit d’en utiliser un et vous avez terminé ou d’utiliser mv pour renommer ... je pense $ mv * *.txt.__ devrait fonctionner, mais testez-le d'abord à petite échelle.

:)

7
Ravi

Vous pouvez peut-être faire quelque chose comme ça avec awk

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

Fondamentalement, il calcule le nom du fichier de sortie en prenant le numéro d’enregistrement (NR) et en le divisant par 5000, en ajoutant 1, en prenant le nombre entier de celui-ci et en complétant le zéro à 2 endroits.

Par défaut, awk imprime l’enregistrement d’entrée entier lorsque vous ne spécifiez rien d’autre. Ainsi, print > outfile écrit l’enregistrement d’entrée entier dans le fichier de sortie.

Comme vous utilisez Windows, vous ne pouvez pas utiliser de guillemets simples car ils n’aiment pas cela. Je pense que vous devez mettre le script dans un fichier puis dire à awkpour utiliser le fichier, quelque chose comme ceci:

awk -f script.awk yourfile

et script.awk contiendra le script comme ceci:

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

Ou bien, cela peut marcher si vous faites ceci:

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

Ce programme de ligne de commande Windows "File Splitter" fonctionne bien: https://github.com/dubasdey/File-Splitter

Il est open source, simple, documenté, éprouvé et a fonctionné pour moi.

Exemple:

fsplit -split 50 mb mylargefile.txt
5
Fabian Kessler

Mon besoin était un peu différent. Je travaille souvent avec des fichiers ASCII séparés par des virgules et par des tabulations, dans lesquels une seule ligne correspond à un seul enregistrement de données. Et comme ils sont vraiment gros, je dois les scinder en parties gérables (tout en préservant la ligne d'en-tête).

Alors, je suis revenu à ma méthode classique VBScript et ai construit ensemble un petit script .vbs qui peut être exécuté sur n’importe quel ordinateur Windows (il est automatiquement exécuté par le moteur hôte de script WScript.exe de Windows).

L'avantage de cette méthode est qu'elle utilise des flux de texte, de sorte que les données sous-jacentes ne sont pas chargées en mémoire (ou, du moins, pas toutes en même temps). Le résultat est qu'il est exceptionnellement rapide et qu'il ne nécessite pas beaucoup de mémoire pour fonctionner. Le fichier de test que je viens de scinder à l'aide de ce script sur mon i7 mesurait environ 1 Go, comptait environ 12 millions de lignes de test et constituait 25 fichiers partiels (chacun contenant environ 500 000 lignes chacun). Le traitement prenait environ N'allez pas plus de 3 Mo de mémoire utilisée à tout moment.

La mise en garde est qu'il repose sur le fichier texte ayant des "lignes" (ce qui signifie que chaque enregistrement est délimité par un CRLF) car l'objet Flux de texte utilise la fonction "ReadLine" pour traiter une seule ligne à la fois. Mais bon, si vous travaillez avec des fichiers TSV ou CSV, c'est parfait.

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

en voici un qui ne manque pas de mémoire lorsqu’il est divisé en gros morceaux! Je devais diviser 95M fichier en 10M x fichiers de ligne.

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

J'ai créé un programme simple pour cela et votre question m'a aidé à compléter la solution ... J'ai ajouté une fonctionnalité supplémentaire et quelques configurations . configurable). Veuillez parcourir les notes . J'ai ajouté les fichiers de code: https://github.com/mohitsharma779/FileSplit

0
User M