Как я могу получить точно n случайные строки из файла с Perl?

@ означает, что соединение должно использовать «Абстрактное пространство имен», а не файловую систему / путь к файлу.

Это устраняет необходимость существующего пути к файловой системе или проблем с разрешениями, имя доступно глобально для подключения / привязки и очищается при удалении всех ссылок.

Это концепция только для Linux.

6
задан Community 23 May 2017 в 12:30
поделиться

7 ответов

Вот хороший однопроходный алгоритм, который я только что придумал, с временной сложностью O (N) и пространственной сложностью O (M) для чтения M строк из N-строчного файла.

Предположим, M <= N.

  1. Пусть S будет набором выбранных линий. Инициализируйте S первыми M строками файла. Если порядок конечного результата важен, перемешайте S сейчас.
  2. Прочтите в следующей строке l . Пока что мы прочитали n = M + 1 строк. Таким образом, вероятность того, что мы хотим выбрать l в качестве одной из наших последних строк, равна M / n .
  3. Принять l с вероятностью M / п ; используйте ГСЧ, чтобы решить, принять или отклонить l .
  4. Если l был принят, случайным образом выберите одну из строк в S и замените ее на l .
  5. Повторяйте шаги 2–4 до тех пор, пока файл не будет исчерпан, увеличивая n ] с каждой новой прочитанной строкой.
  6. Возвращает набор S выбранных строк.
5
ответ дан 10 December 2019 в 00:44
поделиться

Это принимает единственный аргумент командной строки, который представляет собой номер строки, которую вы хотите, 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;
2
ответ дан 10 December 2019 в 00:44
поделиться

Possible solution:

  1. scan one time to count the number of lines
  2. decide the line number to pick randomly
  3. scan again, pick the line
1
ответ дан 10 December 2019 в 00:44
поделиться

В псевдокоде:

use List::Util qw[shuffle];

# read and shuffle the whole file
@list = shuffle(<>);

# take the first 'n' from the list
splice(@list, ...);

Это наиболее тривиальная реализация, но сначала вам нужно прочитать весь файл, для чего потребуется достаточно доступной памяти.

0
ответ дан 10 December 2019 в 00:44
поделиться
@result = ();

$k = 0;
while(<>) {
    $k++;
    if (scalar @result < $n) {
        push @result, $_;
    } else {
        if (rand <= $n/$k) {
            $result[int rand $n] = $_;
        }
    }
}

print for @result;
1
ответ дан 10 December 2019 в 00:44
поделиться

Нет необходимости знать фактический номер строки в файле. Просто найдите случайное место и сохраните следующую строку. (Текущая строка, скорее всего, будет неполной.)

Этот подход должен быть очень быстрым для больших файлов, но не будет работать для 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 $_;
}
1
ответ дан 10 December 2019 в 00:44
поделиться

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>;
}
0
ответ дан 10 December 2019 в 00:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: