テキストファイルを分割したいときってありますよね。
例えばSQLのINSERT INTO hoge VALUES
に続く行が数万行ある時とか (そんなにない)
個人的に上記をやりたいタイミングがあって、npmでいい感じのモジュールを探したんですが
意外とテキストファイルを「指定した行数で」区切ってくれるやつが無かったので泣く泣く自分で作りました。
あ、Nodeです。
Streamで順次処理してるので、でっかいファイルでもヒープアウトしないはず!
Usage
hoge.ts
import{FileSplitter}from"path/to/file-splitter"// your-file.txtを100行ごとに分割するconstfileSplitter=newFileSplitter("/path/to/your-file.txt",100)// 分割開始!fileSplitter.start()
御託は良いからコードを見せろ
file-splitter.ts
importfsfrom"fs"importpathfrom"path"importreadlinefrom"readline"exportclassFileSplitter{privatemaxLines:numberprivateidentifier=0privatecurLine=0privatelineReader:readline.InterfaceprivatecurrentWriteStream:fs.WriteStreamprivateoutputPath:stringconstructor(readFrom:string,maxLines=500){// 分割したファイル群を保存する先のディレクトリを作り、そのpathを保存this.setOutputDir(readFrom)this.maxLines=maxLines// 最初のWriteStreamを作っておくthis.replaceWriteStream()// readlineに指定ファイルのReadStreamを食わせて行リーダーを作るthis.lineReader=readline.createInterface({input:fs.createReadStream(readFrom)})}start(){this.lineReader.on("line",(line)=>{// 現在の行数を保持 (1 ~ 指定行数 + 1までの値を取る)this.curLine++// ここ、もう少しうまくやりたかった人生だったconstisOn=this.curLine===this.maxLinesconstisOver=this.curLine>this.maxLinesif(isOver){// 指定行数を超えたら新しいファイル向けのWriteStreamに切り替え、行数を1にリセットするthis.replaceWriteStream()this.curLine=1}if(isOn){// そのファイル最後の行は改行無しにしている (が、例えば100行のファイルを30行とかで区切られると、最後のファイルには改行が入っちゃう。めんどくさくてこれ以上考えなかった)this.currentWriteStream.write(line)}else{this.currentWriteStream.write(`${line}\n`)}}).on("close",()=>{// 一応掃除するthis.closeWriteStreamIfExists()console.info("Done!")})}privatesetOutputDir(readFrom:string){constextension=path.extname(readFrom)constfileName=path.basename(readFrom,extension)constsplitFileBaseDir=`${path.dirname(readFrom)}/split-files`this.outputPath=`${splitFileBaseDir}/${fileName}`if(!fs.existsSync(splitFileBaseDir)){fs.mkdirSync(splitFileBaseDir)}if(!fs.existsSync(this.outputPath)){fs.mkdirSync(this.outputPath)}}privatereplaceWriteStream():void{this.identifier++this.closeWriteStreamIfExists()constwriteTo=`${this.outputPath}/file_${this.identifier}.txt`console.info(`Start writing to ${writeTo}.\n`)this.currentWriteStream=fs.createWriteStream(writeTo)}privatecloseWriteStreamIfExists(){if(this.currentWriteStream){this.currentWriteStream.close()}}}
まとめ
readline標準モジュールにたどり着くまでの人生を無駄にした。
Streamをちゃんと考えて使ったことあんまりなかったのでちょっと楽しかった。