ファイルを指定した行数ごとに分割するためには、Linuxのsplitコマンドを使えば簡単に実現できます。
PHPではexec関数にsplitコマンドを渡して実行すればよいですが、下記の弱点があります。
- Linuxのコマンドに依存 (PHPの場合はほとんどLinux環境で動作させることが普通なのでそこまで問題にならないかも知れません)。
- exec関数は慎重に引数を渡さないと、OSコマンドインジェクション脆弱性を引き起こす可能性がある。
そこで、今回はPHPでファイルを指定した行数ごとに分割するプログラムを書いてみました。
<?php
class FileSplitter
{
private $lines;
private $fileCount;
public function split($filePath, $linesPerFile, $outputDir)
{
$this->fileCount = 0;
$this->lines = null;
$file = new \SplFileObject($filePath);
$lineCount = 0;
try{
while (!$file->eof())
{
if($lineCount % $linesPerFile === 0)
{
$this->writeToFile($this->generateOutputFilePath($outputDir, $file));
}
$this->lines[] = $file->fgets();
$lineCount++;
}
$this->writeToFile($this->generateOutputFilePath($outputDir, $file));
}
finally
{
$file = null;
}
}
private function generateOutputFilePath($outputDir, \SplFileObject $file)
{
$baseName = $file->getBasename('.'.$file->getExtension());
$ext = $file->getExtension();
return $outputDir.$baseName.'_'.$this->fileCount.'.'.$ext;
}
private function writeToFile($filePath)
{
if($this->lines)
{
file_put_contents($filePath, $this->lines);
$this->fileCount++;
$this->lines = null;
}
}
}
下記は使用例です。
// /tmp/test.txtファイルを100行ごとに分割して、/tmp/output/へ分割したファイルを出力する
(new FileSplitter())->split('/tmp/test.txt'), 100, '/tmp/output/');
// 下記のようにファイルが出力されるはずです。
// /tmp/output/test_0.txt
// /tmp/output/test_1.txt
// /tmp/output/test_2.txt
// ....
拡張としては、ヘッダのあるcsvファイルのヘッダを、分割されたcsvファイルに付加するといったものが考えられます。
この場合は、FileSplitter::splitメソッド内で最初の行をヘッダとみなして保存して、FileSplitter::writeToFileメソッドで毎回ヘッダとして出力すれば実現できます。
コメント