【问题标题】:Read space delimited text file into array of hashes [Perl]将空格分隔的文本文件读入哈希数组 [Perl]
【发布时间】:2019-10-22 21:14:31
【问题描述】:

我有符合以下格式的文本文件:

1 4730 1031782 init
4 0 6 events
2190 450 0 top
21413 5928 1 sshd
22355 1970 2009 find 

我需要将它读入 perl 中的数据结构,以便我根据这些列中的任何一个进行排序和打印。

从左到右,这些列是process_id、memory_size、cpu_time 和program_name。

如何读取具有类似格式的文本文件,以允许我对数据结构进行排序并根据排序进行打印?

到目前为止我的尝试:

my %tasks;

sub open_file{
  if (open (my $input, "task_file" || die "$!\n")){
  print "Success!\n";
  while( my $line = <$input> ) {
    chomp($line);
    ($process_id, $memory_size, $cpu_time, $program_name) = split( /\s/, $line, 4);
    $tasks{$process_id} = $process_id;
    $tasks{$memory_size} = $memory_size;
    $tasks{$cpu_time} = $cpu_time;
    $tasks{$program_name} = $program_name;
    print "$tasks{$process_id} $tasks{$memory_size} $tasks{$cpu_time} $tasks{$program_name}\n";
} 

这确实可以正确打印输出,但是我不知道如何按特定列(即 process_id 或任何其他列)对生成的 %tasks 哈希进行排序并以排序格式打印整个数据结构.

【问题讨论】:

  • 使用数组数组的解决方案是否有用或者您需要哈希?
  • 对数组数组完全开放
  • 使用来自 GNU coreutils 的 /usr/bin/sort:sort -k1 -n so-56493032.csv

标签: arrays perl sorting hash


【解决方案1】:

您将值存储在与值相等的键下。使用Data::Dumper检查结构:

use Data::Dumper;
# ...
print Dumper(\%tasks);

您可以将 pid 存储在哈希哈希中,使用每列的值作为内键。

#!/usr/bin/perl
use strict;
use warnings;
use feature qw{ say };

my @COLUMNS = qw( memory cpu program );
my %sort_strings = ( program => sub { $a cmp $b } );

my (%process_details, %sort);
while (<DATA>) {
    my ($process_id, $memory_size, $cpu_time, $program_name) = split;
    $process_details{$process_id} = { memory  => $memory_size,
                                      cpu     => $cpu_time,
                                      program => $program_name };
    undef $sort{memory}{$memory_size}{$process_id};
    undef $sort{cpu}{$cpu_time}{$process_id};
    undef $sort{program}{$program_name}{$process_id};
}

say 'By pid:';
say join ', ', $_, @{ $process_details{$_} }{@COLUMNS}
    for sort { $a <=> $b } keys %process_details;

for my $column (@COLUMNS) {
    say "\nBy $column:";
    my $cmp = $sort_strings{$column} || sub { $a <=> $b };
    for my $value (sort $cmp keys %{ $sort{$column} }
    ) {
        my @pids = keys %{ $sort{$column}{$value} };
        say join ', ', $_, @{ $process_details{$_} }{@COLUMNS}
            for @pids;
    }
}

__DATA__
1 4730 1031782 init
4 0 6 events
2190 450 0 top
21413 5928 1 sshd
22355 1970 2009 find

但是,如果数据不是很大并且排序不是时间关键,那么仅按给定列对整个数组数组进行排序会更容易编写和阅读:

#!/usr/bin/perl
use strict;
use feature qw{ say };
use warnings;

use enum qw( PID MEMORY CPU PROGRAM );
my @COLUMN_NAMES = qw( pid memory cpu program );
my %sort_strings = ((PROGRAM) => 1);

my @tasks;
push @tasks, [ split ] while <DATA>;

for my $column_index (0 .. $#COLUMN_NAMES) {
    say "\nBy $COLUMN_NAMES[$column_index]:";
    my $sort = $sort_strings{$column_index}
        ? sub { $a->[$column_index] cmp $b->[$column_index] }
        : sub { $a->[$column_index] <=> $b->[$column_index] };
    say "@$_" for sort $sort @tasks;
}
__DATA__
...

您需要安装enum 分发版。

【讨论】:

  • 谢谢 - 我收到编译错误,说不允许使用裸字程序?
  • 实际上我关闭了 strict 并且它有效,谢谢。你知道如何按字母顺序对 program_name 进行排序吗?
  • 关闭严格是不明智的。我的程序已经按程序名称按字母顺序排序,您只需为第二个安装enum。
【解决方案2】:

我不知道如何按特定列对生成的 %tasks 哈希进行排序

您无法对哈希进行排序。您需要将每个输入行转换为哈希(您已成功完成),然后将所有这些哈希存储在一个数组中。然后,您可以按排序顺序打印数组的内容。

这似乎是你想要的:

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my @cols = qw[process_id memory_size cpu_time program_name];

@ARGV or die "Usage: $0 [sort_order]\n";

my $sort = lc shift;

if (! grep { $_ eq $sort } @cols ) {
  die "$sort is not a valid sort order.\n"
    . "Valid sort orders are: ", join('/', @cols), "\n";
}


my @data;

while (<DATA>) {
  chomp;
  my %rec;
  @rec{@cols} = split;
  push @data, \%rec;
}

if ($sort eq $cols[-1]) {
  # Do a string sort
  for (sort { $a->{$sort} cmp $b->{$sort} } @data) {
    say join ' ', @{$_}{@cols};
  }
} else {
  # Do a numeric sort
  for (sort { $a->{$sort} <=> $b->{$sort} } @data) {
    say join ' ', @{$_}{@cols};
  }
}

__DATA__
1 4730 1031782 init
4 0 6 events
2190 450 0 top
21413 5928 1 sshd
22355 1970 2009 find 
  • 我使用了内置的DATA 文件句柄来简化代码。您需要用一些代码替换它才能从外部文件中读取。
  • 我使用散列片来简化将数据读入散列的过程。
  • 您要排序的列作为命令行参数传递到程序中。
  • 请注意,您必须使用字符串比较对最后一列(程序名称)进行排序,并使用数字比较对所有其他列进行排序。

【讨论】:

    【解决方案3】:

    这决定了如何使用脚本接收的第一个参数进行排序。

    #!/usr/bin/env perl
    use strict;
    use warnings;
    use feature 'say';
    
    open my $fh, '<', 'task_file';
    my @tasks;
    my %sort_by = (
        process_id=>0,
        memory_size=>1,
        cpu_time=>2,
        program_name=>3
    );
    my $sort_by = defined $sort_by{defined $ARGV[0]?$ARGV[0]:0} ? $sort_by{$ARGV[0]} : 0;
    while (<$fh>) {
        push @tasks, [split /\s+/, $_];
    }
    @tasks = sort {
        if ($b->[$sort_by] =~ /^[0-9]+$/ ) {
            $b->[$sort_by] <=> $a->[$sort_by];
        } else {
            $a->[$sort_by] cmp $b->[$sort_by];
        }
    } @tasks;
    for (@tasks) {
        say join ' ', @{$_};
    }
    

    【讨论】:

    • 这似乎打印得很完美,但你知道如何按字母顺序排序吗?
    • 嗯,抱歉,它似乎仍然按非字母顺序打印?
    • 好的,我已经更改了,但打印仍然不正确
    • @DavideLorino 您是否将 program_name 参数传递给脚本?
    • 是的,我在排序中指定了参数 3
    猜你喜欢
    • 1970-01-01
    • 2011-11-29
    • 2021-02-26
    • 1970-01-01
    • 2015-12-03
    • 1970-01-01
    • 2016-07-23
    • 2019-10-30
    • 2016-08-19
    相关资源
    最近更新 更多