Подтвердить что ты не робот

Получение уникальной случайной строки (при каждом запуске script) из текстового файла с perl

Наличие текстового файла, такого как следующий, который называется "input.txt"

some field1a | field1b | field1c
...another approx 1000 lines....
fielaNa | field Nb | field Nc

Я могу выбрать любой разделитель поля.

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

Мое решение: я добавил один столбец в файл, поэтому

0|some field1a | field1b | field1c
...another approx 1000 lines....
0|fielaNa | field Nb | field Nc

и обработать его следующим кодом:

use 5.014;
use warnings;
use utf8;
use List::Util;
use open qw(:std :utf8);
my $file = "./input.txt";

#read all lines into array and shuffle them
open(my $fh, "<:utf8", $file);
my @lines = List::Util::shuffle map { chomp $_; $_ } <$fh>;
close $fh;

#search for the 1st line what has 0 at the start
#change the 0 to 1
#and rewrite the whole file

my $random_line;
for(my $i=0; $i<=$#lines; $i++) {
    if( $lines[$i] =~ /^0/ ) {
        $random_line = $lines[$i];
        $lines[$i] =~ s/^0/1/;
        open($fh, ">:utf8", $file);
        print $fh join("\n", @lines);
        close $fh;
        last;
    }
}
$random_line = "1|NO|more|lines" unless( $random_line =~ /\w/ );

do_something_with_the_fields(split /\|/, $random_line))
exit;

Это рабочее решение, но не очень приятное, потому что:

  • порядок строк меняется при каждом запуске script
  • не параллельный script -run safe.

Как написать это более эффективно и элегантно?

4b9b3361

Ответ 1

Эта программа использует модуль Tie::File, чтобы открыть файл input.txt, а также файл indices.txt.

Если indices.txt пуст, он инициализируется индексами всех записей в input.txt в порядке перемешивания.

Каждый запуск, индекс в конце списка удаляется и отображается соответствующая запись ввода.

use strict;
use warnings;

use Tie::File;
use List::Util 'shuffle';

tie my @input, 'Tie::File', 'input.txt'
        or die qq(Unable to open "input.txt": $!);

tie my @indices, 'Tie::File', 'indices.txt'
        or die qq(Unable to open "indices.txt": $!);

@indices = shuffle(0..$#input) unless @indices;

my $index = pop @indices;
print $input[$index];

Обновление

Я изменил это решение так, что он заполняет новый файл indices.txt только в том случае, если он еще не существует, а не, как и раньше, просто, когда он пуст. Это означает, что новая последовательность записей может быть напечатана просто путем удаления файла indices.txt.

use strict;
use warnings;

use Tie::File;
use List::Util 'shuffle';

my ($input_file, $indices_file) = qw( input.txt indices.txt );

tie my @input, 'Tie::File', $input_file
        or die qq(Unable to open "$input_file": $!);

my $first_run = not -f $indices_file;

tie my @indices, 'Tie::File', $indices_file
        or die qq(Unable to open "$indices_file": $!);

@indices = shuffle(0..$#input) if $first_run;

@indices or die "All records have been displayed";
my $index = pop @indices;
print $input[$index];

Ответ 2

Как насчет сохранения перетасованного списка номеров строк в другом файле, удаляя первый каждый раз, когда вы его используете? Некоторая блокировка может потребоваться для обеспечения совместимости безопасности script -run.

Ответ 3

От perlfaq5.

Как выбрать случайную строку из файла?

Задержка загрузки файла в базу данных или предварительная индексация строк в файл, есть несколько вещей, которые вы можете сделать.

Здесь используется алгоритм выборки коллектора из книги Camel:

srand;
rand($.) < 1 && ($line = $_) while <>;

Это имеет существенное преимущество в пространстве над чтением всего файла в. Вы можете найти доказательство этого метода в "Искусстве компьютера" Программирование, том 2, раздел 3.4.2, Дональд Э. Кнут.

Вы можете использовать модуль File:: Random, который предоставляет функцию для этого Алгоритм:

use File::Random qw/random_line/;
my $line = random_line($filename);

Другой способ - использовать модуль Tie:: File, который обрабатывает весь файл как массив. Просто доступ к элементу случайного массива.

Все программисты на Perl должны найти время, чтобы прочитать FAQ.

Обновление: Чтобы получить уникальную случайную строку каждый раз, когда вам нужно будет сохранить состояние. Самый простой способ сохранить состояние - удалить строки, которые вы использовали из файла.