使用perl从文本文件中获取唯一的随机行(在每个脚本运行时)

nov*_*cik 12 perl

有一个像下一个名为"input.txt"的文本文件

some field1a | field1b | field1c
...another approx 1000 lines....
fielaNa | field Nb | field Nc
Run Code Online (Sandbox Code Playgroud)

我可以选择任何字段分隔符.

需要一个脚本,每个离散运行的内容将从该文件中获得一个唯一(从不重复)的随机行,直到使用所有行.

我的解决方案:我在文件中添加了一列,所以有

0|some field1a | field1b | field1c
...another approx 1000 lines....
0|fielaNa | field Nb | field Nc
Run Code Online (Sandbox Code Playgroud)

并使用下一个代码处理它:

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;
Run Code Online (Sandbox Code Playgroud)

这是一个有效的解决方案,但不是很好,因为:

  • 每个脚本运行时行顺序都在变化
  • 不是并发脚本运行安全.

如何写得更有效,更优雅?

cho*_*oba 8

如何在一个不同的文件中保留一个洗牌的行号列表,每次使用它时删除第一个行号?可能需要一些锁定才能确保脚本运行安全.


Bor*_*din 3

该程序使用该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];
Run Code Online (Sandbox Code Playgroud)

更新

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];
Run Code Online (Sandbox Code Playgroud)