これがPerlソリューションです。
私は最近これをたくさん書いているようですが、非常に大きなテキストファイルにインデックスを付けることは、ファイル全体をメモリに読み込まずにランダムアクセスを取得するための最良の方法です。
このプログラムは、tell
演算子を使用してソースファイル内の現在のレコードのオフセットを確立し、演算子を使用seek
して特定のレコードに戻り、vec
選択されたレコードを追跡します。
do { ... } while EXPR
フォームは、最初に条件をチェックする前にdo-blockを実行し、この目的のために特別に選択されていることに注意してください。
プログラムは、コマンドラインで指定されたデータについてファイルがスキャンされることを想定しています。出力ファイルはselected.txt
、選択された60%とunselected.txt
残りのファイル用です。
use strict;
use warnings;
my $file = shift or die "No input file specified";
open my $infh, '<', $file or die qq(Unable to open "$file" for input: $!);
my @index;
do { push @index, tell $infh } while <$infh>;
my $used = "\0" x (@index / 8 + 1);
my $outfh;
open $outfh, '>', 'selected.txt' or die $!;
my $n = 0;
while ($n++ / @index < 0.6) {
my $rec = int rand scalar @index;
seek $infh, $index[$rec], 0;
print $outfh scalar <$infh>;
vec($used, $rec, 1) = 1;
}
open $outfh, '>', 'unselected.txt' or die $!;
for my $rec (0 .. $#index) {
next if vec($used, $rec, 1);
seek $infh, $index[$rec], 0;
print $outfh scalar <$infh>;
}
編集
私はモジュールを使用して非常に小さなコードを置き換えることを躊躇しますが、誰かがこのアプローチを好む場合に備えて、池上が推奨するようにTie::File
使用するバージョンがあります。
use strict;
use warnings;
use Tie::File;
my $file = shift or die "No input file specified";
tie my @index, 'Tie::File', $file, mode => O_RDONLY
or die qq(Unable to open "$file" for input: $!);
my $outfh;
my @used;
open $outfh, '>', 'selected.txt' or die $!;
my $n = 0;
while ($n++ / @index < 0.6) {
my $rec = int rand scalar @index;
print $outfh $index[$rec], "\n";
$used[$rec]++;
}
open $outfh, '>', 'unselected.txt' or die $!;
for my $rec (0 .. $#index) {
print $outfh $index[$rec], "\n" unless $used[$rec];
}