使用相同的变量在 Perl 的 foreach 循环中打开和关闭文件

Using the same variable to open and close files in foreach loop in Perl

考虑到变量声明,我很难理解这个问题。场景:我有一个包含十个单词的文件,每行一个。首先,我想遍历文件并根据数据创建新文件。例子

banana
apple
coconut
strawberry

-->

banana.txt
apple.txt
coconut.txt
strawberry.txt

我遇到的第一个问题是:如何为循环中的每个文件的文件句柄分配一个唯一变量?我会写这样的东西但是不知道这样行不行:

open(my $tokensfh, '<', $tokensfile)
  or die "cannot open file $tokensfile";
chomp(my @tokenslines = <$tokensfh>);
close $tokensfh;

foreach my $token(@tokenslines) {
  open(my $token.'fh', '>>', $token."data.txt");
} 

再往下一点,我将其他数据与 $token 进行匹配,但我不确定如何处理变量:

foreach my $somedata(@data) {
    my $datatoken = $somedata=~  /<fruit>(.+)<\/fruit>/;

    # Do I need a new variable name here?
    foreach my $tokensline(@tokenslines) {
        if ($datalinetoken eq $datatoken ) {
          # print $somedata to specific file
          print $tokensline.'fh' "average run time\n";
        }
    }
}

我需要一个新的变量名吗?如果不需要,我怎样才能重新使用之前的变量而不会出现变量赋值问题?有一个更好的方法吗? (请回答所有问题。)

不要这样做。使用可变的变量名是非常讨厌的。有关原因的更详细解释,请参阅此 link:http://perl.plover.com/varvarname.html

如果您需要命名的文件句柄,最好使用文件句柄的散列。散列是一个可移植的命名空间,这正是您在这里所需要的。

所以:

my %fh_for; 
foreach my $token ( @tokenlines ) { 
   open ( my $fh_for{$token}, '>', "$token.txt" ) or die $!; 
}

foreach my $datalinetoken (@tokenslines) {
    if ($datalinetoken eq $datatoken ) {
      # print $somedata to specific file
      print {$fh_for{$datalinetoken}} "average run time\n";
    }
}

然后您可以写入由您的令牌名称键入的文件句柄,而无需动态变量命名的令人讨厌的混乱。请注意,我已将您的 fh 包含在 {} 中 - 有必要告诉 perl 到 'evaluate this'。

您可以重复使用相同的全局变量名,只要它们在不同的作用域中声明即可。如果你两次声明同一个变量,Perl 会警告你。我在下面的代码中使用了相同的名称 $fh 作为文件句柄,没有任何后果

在这种情况下,你需要打开大部分程序的文件句柄,所以你需要一整套,看起来使用哈希最简单,这样你就可以选择正确的通过使用令牌字符串

索引散列来处理文件句柄

它看起来像这样。请注意,我使用 use autodie 来避免必须显式检查每个 IO 操作的状态。您可能还想考虑是否需要处理 appleAPPLEApple 之间的差异,目前这将创建三个文件句柄(并且会混淆 Windows 太可怕了!)

哦,顺便说一下,用 while 逐行处理每个文件比将其全部读入数组并从那里处理数据要好得多

use strict;
use warnings 'all';
use v5.14.1; # For autodie
use autodie;

use constant TOKENS_FILE => 'tokens.txt';
use constant XML_FILE    => 'data.xml';

my %token_fh;

{
    open my $fh, '<', TOKENS_FILE;

    while ( <$fh> ) {
        chomp;
        open $token_fh{$_}, '>', "${_}data.txt";
    }
}

{
    open my $fh, '<', XML_FILE;

    while ( <$fh> ) {

        next unless my ($token) = m|<fruit>(.+)</fruit>|;
        next unless my $fh = $token_fh{$token};

        print $fh "average run time\n";
    }
}

close $_ for values %token_fh;



另一种方法是完全忘记令牌文件,只打开在 XML 中遇到的文件。看起来像这样

use strict;
use warnings 'all';
use v5.14.1; # For autodie
use autodie;

use constant XML_FILE    => 'data.xml';

my %token_fh;

open my $fh, '<', XML_FILE;

while ( <$fh> ) {

    next unless my ($token) = m|<fruit>(.+)</fruit>|;

    unless ( exists $token_fh{$token} ) {
        open $token_fh{$token}, '>', "${token}data.txt";
    }
    my $fh = $token_fh{$token};

    print $fh "average run time\n";
}

close $_ for values %token_fh;