私は約100,000行のテキストファイルを複数持っていますが、それらをそれぞれ5000行の小さなテキストファイルに分割したいのです。
私は使った:
split -l 5000 filename.txt
それはファイルを作成します。
xaa
xab
aac
xad
xbe
aaf
拡張子のないファイル私はそれらを以下のように呼びたいだけです。
file01.txt
file02.txt
file03.txt
file04.txt
それが不可能であれば、私は単にそれらが ".txt"拡張子を持つようにしたい。
私はこの質問がずっと前にされたことを知っています、しかし私は誰も最も直接的なunix答えを与えなかったことに驚きます:
split -l 5000 -d --additional-suffix=.txt $FileName file
-l 5000
:ファイルをそれぞれ5,000行のファイルに分割します。-d
:数字の接尾辞これにより、デフォルトでaaからzzの代わりに00から99に接尾辞が付けられます。--additional-suffix
:接尾辞、ここでは拡張子を指定できます$FileName
:分割するファイルの名前.file
:結果のファイルに追加する接頭辞。いつものように、詳細についてはman split
をチェックしてください。
Macの場合、デフォルトバージョンのsplit
は明らかに見捨てられています。次のコマンドを使用してGNUバージョンをインストールできます。 ( より多くのGNU utilsについてはこの質問を参照してください )
brew install coreutils
そして、split
をgsplit
に置き換えることで上記のコマンドを実行できます。詳細はman gsplit
を調べてください。
これがC#の例です(これが私が探していたものです)。ファイルを見るには、23 GBのcsvファイルを約1億7,500万行で分割する必要がありました。私はそれをそれぞれ100万行のファイルに分割しました。このコードは私のマシンで約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);
@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分未満)、または文字の感度をバッチ処理することはできません。本当にターゲットデータの特性に依存します。
テストには、100K行のデータを含むq25249516.txt
という名前のファイルを使用しました。
改訂版のより速いバージョン
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
テストにはllimit
の50000を使用したことに注意してください。 llimit
* 100がファイルの行数よりも大きい場合は、初期のファイル番号を上書きします(fcount
を1999
に設定し、ファイル名変更行で~3
の代わりに~2
を使用してください)。
あなたはおそらく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
構文は次のようになります。
$ split [OPTION] [INPUT [PREFIX]]
プレフィックスはPREFIXaa、PREFIXab、...です。
適切なものを使用するだけで完了です。または名前の変更にはmvを使用してください。私は$ mv * *.txt
はうまくいくはずだと思いますが、最初に小規模でテストします。
:)
この「ファイルスプリッタ」Windowsコマンドラインプログラムはうまく機能します。 https://github.com/dubasdey/File-Splitter
それはオープンソースで、シンプルで、文書化され、証明され、そして私のために働きました。
例:
fsplit -split 50 mb mylargefile.txt
私の要求は少し違いました。 1行が1レコードのデータであるComma DelimitedとTab Delimited ASCIIファイルでよく作業します。そしてそれらは本当に大きいので、(ヘッダ行を保存しながら)それらを管理しやすい部分に分割する必要があります。
それで、私は私の古典的なVBScriptメソッドに戻って、そしてどんなWindowsコンピュータでも実行できる小さい.vbsスクリプトを一緒にまとめました(それは、Windows上のWScript.exeスクリプトホストエンジンによって自動的に実行されます)。
このメソッドの利点は、テキストストリームを使用しているため、基になるデータがメモリに読み込まれないことです(少なくとも、一度にすべてが読み込まれることはありません)。その結果、これは非常に高速であり、実行にはそれほど多くのメモリーを必要としません。私のi7でこのスクリプトを使って分割したばかりのテストファイルは、約1GBのファイルサイズで、約1,200万行のテストがあり、25個のパートファイル(それぞれ約500k行)が作成されました。どの時点でも3 MB以上のメモリを使用しないでください。
ここでの注意点は、Text Streamオブジェクトは一度に1行ずつ処理するために "ReadLine"関数を使用するため、 "lines"を持つテキストファイル(各レコードは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())
これはc#の中の1つで、大きなチャンクに分割してもメモリ不足にはなりません。私は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();
私はこれのための簡単なプログラムを作成しました、そして、あなたの質問は私が解決を完成するのを助けました...私はもう一つの特徴といくつかの構成を加えました。数行ごとに特定の文字/文字列を追加したい場合(設定可能)。メモを確認してください。私はコードファイルを追加しました: https://github.com/mohitsharma779/FileSplit