perl 将哈希的键作为数组,并在匹配后将值作为数组的键值

9gm1akwq  于 2023-03-09  发布在  Perl
关注(0)|答案(2)|浏览(209)

我已经把显示的文件作为脚本的输入,并创建了一个散列。我想把键作为数组,并在匹配后把相应的键值放入数组。
我已经创建了散列,但发现问题,创建数组键和它的值到相应的数组。任何帮助是非常感谢。
预期输出如下

@school = (STRING_x, STRING_y, STRING_z,STRING_k)
@University = (STRING_a, STRING_b, STRING_c)s
@College = (STRING_d, STRING_e, STRING_f)

输入文件
x一个一个一个一个x一个一个二个x

b4lqfgs4

b4lqfgs41#

可能是这样的。它分两个阶段处理文件。
第一阶段解析输入数据,提取对我们有用的信息,并将其存储在散列中。散列的键是数组名,散列中的值是包含数组元素的数组引用。
阶段2遍历我们在阶段1中构建的哈希,并将其转化为所需的输出。

#!/usr/bin/perl

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

# Where we're going to store the useful bits of the
# data during the parsing stage
my %data;
# Where we store the name of the current hash key.
my $key;

# Stage 1: Parsing
while (<>) {
  # This looks for the array name record, extracts the
  # string from between "Type" and "Authors" and stores that
  # string as $key
  if (/Type:,(\w+),Authors:/) {
    $key = $1;
  }

  # This looks for "STRING" record, extracts that from the input
  # line and pushes that onto the end of the current array reference
  if (/(STRING[^,]+)/) {
    push @{$data{$key}}, $1;
  }
}

# Stage 2: Output
# For each key in the data hash...
for (keys %data) {
  # ... extract the data we want and print it
  say '@', "$_ = (", join(', ', @{$data{$_}}), ')';
}

**更新:**好了,让我们更详细地看看最后一个循环。

我们有一个哈希值%data,它包含了我们从输入文件中解析出来的信息,键是“学校”和“学院”,值是一个数组引用。
我们从遍历哈希的键开始,我们可以打印这些键。

foreach (keys %data) {
  say $_;
}

注意,每次循环时,变量$_都包含一个哈希值的键,因为哈希值是随机排序的,所以每次都以随机顺序返回这些键。
您希望键名前面有一个“@",这很简单:

foreach (keys %data) {
  say '@', $_;
}

把它放在一个双引号字符串("@$_")中可能很有诱惑力,但是Perl会把“@”解释为数组变量的开头(这里没有),所以最简单的解决方法是把字符串分成两部分。
然后,我们希望字符串具有“=(STUFF)”-所以让我们添加它。

foreach (keys %data) {
  say '@', "$_ = (STUFF)";
}

当然,“STUFF”要复杂一些,我们可以从打印散列值开始:

foreach (keys %data) {
  say '@', "$_ = ($data{$_})";
}

但这并没有给予我们想要的:

@College = (ARRAY(0x55c35f9c8fd0))
@University = (ARRAY(0x55c35f9c8e68))
@School = (ARRAY(0x55c35f9d23e8))

这是因为我们有一个数组,reference,所以我们需要解引用它。

for (keys %data) {
  say '@', "$_ = (@{$data{$_}})";
}

这让我们更接近:

@University = (STRING_a STRING_b STRING_c)
@School = (STRING_x STRING_y STRING_z STRING_k)
@College = (STRING_d STRING_e STRING_f)

但是你要求在列表中的元素之间使用逗号,我们可以使用join()来做到这一点,但是我们需要将输出字符串的位分开--因为你不能在字符串中调用函数,如果我们将输出字符串的每一部分放在单独的行中,可能会更容易理解:

for (keys %data) {
  say '@',
      "$_ = (",
      join(', ', @{$data{$_}}),
      ')';
}

我们在这里打印四个部分:
1.首字母“@”
1.键名($_)沿着字符串的下一个固定部分
1.通过调用join()并传递““生成的字符串,以及解引用的数组
1.列表末尾的结束符“)”
把这些放在一起,我们得到这个输出:

@College = (STRING_d, STRING_e, STRING_f)
@School = (STRING_x, STRING_y, STRING_z, STRING_k)
@University = (STRING_a, STRING_b, STRING_c)

有一件事我忘了提,我去掉了你的代码,硬编码的输入文件名和打开文件。它更简单,更灵活,只是从命令行读取数据。

while (<>) {
  ...
}

它只是从命令行上传递的文件名中读取数据,然后将每一行依次放入$_中。
我希望这能让事情更清楚。

mzmfm0qo

mzmfm0qo2#

下面是代码的重组版本。

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use feature qw(say);

my $csv= "./file.txt";

open(CSV, "<", $csv )
    or die "cannot open csv\n";

my %storage;
my $compiler;

my %bil = map { $_ => 1}
          qw(
            STRING_a
            STRING_b
            STRING_c
            STRING_d
            STRING_e
            STRING_f
            STRING_x
            STRING_y
            STRING_z
            STRING_k
        );

while (<CSV>){
    chomp(my $line = $_);

    next
        if $line !~ /^No|^\d+/ ;

    $compiler = $1
        if ($line =~ /No,\d+,Type:,(\w+)/);

    if ($line =~ /\d+,\d+,\d+,(\w+),/){
        my $macro = $1;
        push @{ $storage{$compiler} }, $1;
    }
}

close CSV;
# print Dumper \%storage;

my @school     = grep { $bil{$_} } @{ $storage{School}     } ;
my @University = grep { $bil{$_} } @{ $storage{University} } ;
my @College    = grep { $bil{$_} } @{ $storage{College}    } ;

say "school     [@school]";
say "University [@University]";
say "College    [@College]";

运行将产生以下输出

school     [STRING_x STRING_y STRING_z STRING_k]
University [STRING_a STRING_b STRING_c]
College    [STRING_d STRING_e STRING_f]

如果compilers的完整列表(即学校、大学、学院)仅在file.txt中可用,则可以按如下所示修改代码,使其创建一个results散列,用于存储每个compilers的数据

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use feature qw(say);

my $csv= "./file.txt";

open(CSV, "<", $csv )
    or die "cannot open csv\n";

my %storage;
my $compiler;
my @compilers;

my %bil = map { $_ => 1}
          qw(
            STRING_a
            STRING_b
            STRING_c
            STRING_d
            STRING_e
            STRING_f
            STRING_x
            STRING_y
            STRING_z
            STRING_k
        );

while (<CSV>){
    chomp(my $line = $_);

    next
        if $line !~ /^No|^\d+/ ;

    if ($line =~ /No,\d+,Type:,(\w+)/) {
        $compiler = $1 ;
        push @compilers, $compiler;
    }

    if ($line =~ /\d+,\d+,\d+,(\w+),/){
        my $macro = $1;
        push @{ $storage{$compiler} }, $1;
    }
}

close CSV;

my %results ;

for my $comp (@compilers)
{
    @{ $results{$comp} } = grep { $bil{$_} } @{ $storage{$comp} } ;
}

say Dumper \%results;

输出为

$VAR1 = {
          'University' => [
                            'STRING_a',
                            'STRING_b',
                            'STRING_c'
                          ],
          'School' => [
                        'STRING_x',
                        'STRING_y',
                        'STRING_z',
                        'STRING_k'
                      ],
          'College' => [
                         'STRING_d',
                         'STRING_e',
                         'STRING_f'
                       ]
        };

相关问题