Text::Parts てのを書いた
githubにおいてます。
テキストファイルを複数のパーツに分けるものです(実際に分割するわけではなく、分けた部分を読むためのオブジェクトを返します)。
各パーツは、行頭から始まって行末で終わる感じになります。行の途中で分けられるということはありません。
とても大きいCSVファイルとかを分割する用途とかに使います。既にありそうなもんだけど、無さそうだったんで作りました。
my $s = Text::Parts->new(file => "file.txt"); my @parts = $s->split(num => 4); # num => 割りたい数。size => 割りたいバイト数 も可 foreach my $part (@parts) { my $l = $part->getline; # or <$part> # ... }
て感じで使います。
$part はそのパートのファイルの開始と終了のポジションを持っており、オブジェクト作成時にファイルをopen & seekして、後は、<$fh>しながら、そのパートの最後の位置に達したら終了という感じの簡単なものです。
実際ありそうな、CSVファイルを分割する場合は、次のようにします。
my $s = Text::Parts->new(file => "file.txt", , eol => "\r\n", parser => Text::CSV_XS->new({binary => 1, eol => "\r\n"})); my @parts = $s->split(num => 4); foreach my $part (@parts) { my $columns = $part->getline_parser; # ... }
$columns は、Text::CSV_XS のgetline の戻り値なので、パース後の配列リファレンスとなります。
Text::CSV_PP の場合、どうも壊れたCSVファイルを渡す(aaa", みたいなクォートが不一致なカラムがある)と、ファイルの終端まで読んでしまうので、check_line_start => 1 というオプションを渡してください。このオプションを付けると、行頭まで戻ってから、getline に渡します。
ただ、CSVで改行を含むカラムとかだと、壊れたフォーマットになる可能性はあるので、PP版だとうまく行きません。まぁ、XS版でうまくいってるのも、たまたまっていう話かも。
parser には、ファイルハンドルを受け取るパーサなら何でも渡せます。例えば、log のパーサとかでも良いかもしれない。
parserに渡したオブジェクトで使うメソッドは getline がデフォルトになってます。変更する場合は、parser_method オプションでメソッド名を渡してください。
なお、厳密に何行ずつに分けるのではなくて、ファイルサイズを割りたい数で割った分で分けていきます。
ただ、1行だけ非常に長いといった場合はありますので、4つに分ける場合は、
最初のパートのサイズ -- ファイルサイズ / 4 で試す 次のパートのサイズ -- 残りサイズ / 3 で試す 次のパートのサイズ -- 残りサイズ / 2 で試す ...
のようにして調整して、最初に渡された分割数になるようにしています(サイズで渡した場合も、ファイルサイズから分割数を計算しているので、同じです)。