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".
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.
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);
@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.)
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.
:)
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 à awk
pour 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
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
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())
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();
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