这是一个 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>;
}
编辑
我犹豫是否要使用模块来替换这么少的代码,但这里有一个使用ikegamiTie::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];
}