@ означает, что соединение должно использовать «Абстрактное пространство имен», а не файловую систему / путь к файлу.
Это устраняет необходимость существующего пути к файловой системе или проблем с разрешениями, имя доступно глобально для подключения / привязки и очищается при удалении всех ссылок.
Это концепция только для Linux.
Вот хороший однопроходный алгоритм, который я только что придумал, с временной сложностью O (N) и пространственной сложностью O (M) для чтения M строк из N-строчного файла.
Предположим, M <= N.
S
будет набором выбранных линий. Инициализируйте S
первыми M
строками файла. Если порядок конечного результата важен, перемешайте S
сейчас. l
. Пока что мы прочитали n = M + 1
строк. Таким образом, вероятность того, что мы хотим выбрать l
в качестве одной из наших последних строк, равна M / n
. l
с вероятностью M / п
; используйте ГСЧ, чтобы решить, принять или отклонить l
. l
был принят, случайным образом выберите одну из строк в S
и замените ее на l
. n
] с каждой новой прочитанной строкой. S
выбранных строк. Это принимает единственный аргумент командной строки, который представляет собой номер строки, которую вы хотите, N. Первые N строк заняты, возможно, вы их больше не увидите. После этого вы случайным образом решить, стоит ли переходить на следующую строку. И если вы это сделаете, вы случайным образом решите, какая строка в текущем списке N для перезаписи.
#!/usr/bin/perl
my $bufsize = shift;
my @list = ();
srand();
while (<>)
{
push(@list, $_), next if (@list < $bufsize);
$list[ rand(@list) ] = $_ if (rand($. / $bufsize) < 1);
}
print foreach @list;
Possible solution:
В псевдокоде:
use List::Util qw[shuffle];
# read and shuffle the whole file
@list = shuffle(<>);
# take the first 'n' from the list
splice(@list, ...);
Это наиболее тривиальная реализация, но сначала вам нужно прочитать весь файл, для чего потребуется достаточно доступной памяти.
@result = ();
$k = 0;
while(<>) {
$k++;
if (scalar @result < $n) {
push @result, $_;
} else {
if (rand <= $n/$k) {
$result[int rand $n] = $_;
}
}
}
print for @result;
Нет необходимости знать фактический номер строки в файле. Просто найдите случайное место и сохраните следующую строку. (Текущая строка, скорее всего, будет неполной.)
Этот подход должен быть очень быстрым для больших файлов, но не будет работать для STDIN. Черт возьми, ничего вроде кеширования всего файла в памяти для STDIN не работает. Итак, если у вас должен быть STDIN, я не понимаю, как вы можете быть быстрым / дешевым для больших файлов.
Вы можете обнаружить STDIN и переключиться на кэшированный подход, в противном случае работайте быстро.
#!perl use strict; my $file='file.txt'; my $count=shift || 10; my $size=-s $file; open(FILE,$file) || die "Can't open $file\n"; while ($count--) { seek(FILE,int(rand($size)),0); $_=readline(FILE); # ignore partial line redo unless defined ($_ = readline(FILE)); # catch EOF print $_; }
Here's some verbose Perl code that should work with large files.
The heart of this code is that it does not store the whole file in memory, but only stores offsets in the file.
Use tell
to get the offsets. Then seek
to the appropriate places to recover the lines.
Better specification of target file and number of lines to get is left as an exercise for those less lazy than I. Those problems have been well solved.
#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw(shuffle);
my $GET_LINES = 10;
my @line_starts;
open( my $fh, '<', 'big_text_file' )
or die "Oh, fudge: $!\n";
do {
push @line_starts, tell $fh
} while ( <$fh> );
my $count = @line_starts;
print "Got $count lines\n";
my @shuffled_starts = (shuffle @line_starts)[0..$GET_LINES-1];
for my $start ( @shuffled_starts ) {
seek $fh, $start, 0
or die "Unable to seek to line - $!\n";
print scalar <$fh>;
}