将以空格分隔的文本文件读取为哈希数组[Perl]

时间:2019-06-07 10:58:42

标签: arrays perl sorting hash

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

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

我需要将其读入perl的数据结构中,这将允许我根据任何这些列进行排序和打印。

从左到右,各列分别是process_idmemory_sizecpu_timeprogram_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哈希进行排序,并在排序格式。

3 个答案:

答案 0 :(得分:2)

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

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

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

#!/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发行版。

答案 1 :(得分:1)

  

我不知道如何按特定列对所得的%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文件句柄来简化代码。您需要将其替换为一些代码才能从外部文件读取。
  • 我使用哈希切片来简化将数据读取为哈希的情况。
  • 要作为排序依据的列作为命令行参数传递到程序中。
  • 请注意,您必须使用字符串比较来对最后一列(程序名称)进行排序,而所有其他列都必须使用数字比较来进行排序。

答案 2 :(得分:0)

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

#!/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 ' ', @{$_};
}