【问题标题】:How to identify nth lines of n files in while<>如何在while<>中识别n个文件的第n行
【发布时间】:2016-03-13 17:00:23
【问题描述】:

我有一个在所有文件中添加所有向量的代码。 可以有任意数量的输入文件。例如第一个输入文件是:

0.55        0       0.3335      1.2
0.212       0       2.2025      1

第二个是:

0.25        0       0.3333      1.0
0.1235      0       0.2454      1

我得到的是所有向量的总和,因此我得到一个向量 也就是:

1.13550     0       3.1147      4.2

但我试图将第一个文件的第一个向量与第二个文件的第一个向量相加,依此类推。结果根据这个例子,我应该得到 2 个向量。

现在我有这个:

use strict;
use warnings;

if ($ARGV[0] ne "vector1.dat"){
    die ("vector1.dat is necessary as first argument");
}

my @sum = 0;
my $dim = 0;

while (<>) {

    #Ignore blank lines, hashtags 
    #and lines starting with $
    if ($_ =~ /#/ || $_ =~ /^$/ || $_ =~ /^\s$/){
        next;
    }
    my @vectors = split(" ", $_);
    my $vector_length = @vectors;

    if ($dim eq 0) {
        $dim = $vector_length;
    } 
    else {
        if ($dim ne $vector_length) {
            die ("Vector dimensions do not match. : $!");
        }
    }
    for (my $i = 0; $i <= $#vectors; $i++) {
        $sum[$i] += $vectors[$i];
    }
}

$" = "\t\t";
print "\n --- \n @sum \n";

我需要的只是找出如何识别每个文件的第 n 行 并在记住这些行的列值时求和,可以有 n 个文件。 我在这里看到了类似问题的文件处理问题,但是 我没有在那里找到我的答案。 只是寻找一些建议和指导。卡在这上面了。

【问题讨论】:

  • 你应该在 shebang 线上use warnings 'all' 而不是-w。不要同时使用两者
  • my @sum = 0?那应该是什么意思?第一个元素为 0 的数组?
  • 如果您打算 /^$/ 检查以美元符号 $ 开头的行,那将不起作用,因为 $ 是一个正则表达式元字符。您在下一个正则表达式中偶然显示。
  • @briandfoy 不,这将是第三个寻找空行的正则表达式。 /^\s$/。他在 cmets 中说它是“以 $ 开头的行”。
  • 感谢您对 shebang 的建议,我会记住这一点。关于@sum-好点,绝对没有必要。我会记住,有更好的方法来查找空行,谢谢你的通知。

标签: perl vector sum filehandle


【解决方案1】:

自己打开每个文件并使用$. 变量来了解您在哪一行(或自己计算文件)。这是基本结构:

foreach my $file ( @files ) {
    open my $fh, '<', $file or die ...;
    while( <$fh> ) {
        chomp;
        $sum[ $. ] = ...;  # $. is the line number
        }
    }

如果您不喜欢$.,您可以使用它的长名称。你必须打开English(Perl 自带):

use English;
## use English qw( -no_match_vars );  # for v5.16 and earlier

foreach my $file ( @files ) {
    open my $fh, '<', $file or die ...;
    while( <$fh> ) {
        chomp;
        $sum[ $INPUT_LINE_NUMBER ] = ...;
        }
    }

或者,您可以自己数数,如果文件中的向量没有按严格的行号排列(可能是因为 cmets 或其他一些奇怪的格式),这可能会很方便:

foreach my $file ( @files ) {
    open my $fh, '<', $file or die ...;
    my $line = -1;
    while( <$fh> ) {
        $line++;
        chomp;
        $sum[ $line ] = ...;
        }
    }

更难的方法是the answer bart gives,它在每一行的末尾检查eof,看看神奇的ARGV句柄是否正在查看一个新文件,如果是,则重置$.。这是一个有趣的技巧,但几乎没有人会理解它在做什么(甚至注意到它)。

对于问题的另一部分,我认为你做错了向量求和,或者使用了令人困惑的变量名。一条线是一个向量,线中的数字是一个分量。二维数组将起作用。第一个索引是行号,第二个是组件索引:

while( <$fh> ) {
    chomp;
    ... skip unwanted lines
    my @components = split;
    ... various dimension checks
    foreach my $i ( 0 .. $#components ) {
        $sum[ $. ][ $i ] += $components[ $i ];
        }
    }

Data::Dumper 模块适用于复杂的数据结构。您还可以查看perldsc(Perl Data Structures Cookbook)文档。 $. 变量位于 perlvar 中。

【讨论】:

  • 谢谢,您的解释对我帮助很大。完全没有考虑二维数组,这是一个很好的通知。
  • 如果$_仅用于split(' ', $_),则不需要chomp($_)
  • Re “但几乎没有人会理解”,这就是 cmets 的用途。
  • 如果你真的想避免魔法,你会使用my $input_line_number = $.;而不是use English;
【解决方案2】:

$. 是最近读取的文件句柄的行号。 close(ARGV) if eof; 可用于重置文件之间的文件号(如eof 中所述)。 (注意:eof()eof 不同。)所以你现在有了行号。

您遇到的第二个问题是将向量分量 ($vectors[$i]) 添加到向量 ($sum[$i]) 中。您需要将向量组件添加到向量组件。从使用更合适的变量名开始。

这是我们得到的:

my @sum_vectors;
while (<>) {
   s/#.*//;          # Remove comments.
   next if /^\s*$/;  # Ignore blank lines.

   my @vector = split;

   if ($sum_vectors[$.] && @{ $sum_vectors[$.] } != @vector) {
      die("$ARGV:$.: Vector dimensions do not match\n");
   }

   for my $i (0..$#vector) {
      $sum_vectors[$.][$i] += $vector[$i];
   }
} continue {
   close(ARGV) if eof;  # Reset line numbers for each file.
}

修复了另外两个错误:

  • $! 在您使用时没有包含任何有意义的内容。
  • 您忽略了包含 cmets 的行,即使它们也包含有效数据。

【讨论】:

  • 在声明变量时我会更具体一些,很抱歉用不恰当的名称让读者感到困惑。
  • 不是这样的。选择合适的名称以避免混淆自己很重要。使用我使用的名称,您的错误添加 ($sum_vectors[$i] += $vector[$i];) 看起来不正确。你自己就能判断出问题所在。
  • 确实如此。会小心命名。是的,我已经注意到没用的 $!变量也。至于 cmets,这是一个非常好的观点。感谢您的帮助,您让我注意到了我错过的非常简单但重要的事情。
【解决方案3】:

试试这个:

#!/usr/bin/perl
use strict;
use warnings;

if ($ARGV[0] ne "vector1.dat"){
    die ("vector1.dat is necessary as first argument");
}

my %sum;
my $dim = 0;
my $vector_length;
my $line_number;

while (<>) {

    #Ignore blank lines, hashtags
    #and lines starting with $
    if ($_ =~ /#/ || $_ =~ /^$/ || $_ =~ /^\s$/){
        next;
    }
    my @vectors = split(" ", $_);
    $vector_length = @vectors;

    if ($dim eq 0) {
        $dim = $vector_length;
    }
    else {
        if ($dim ne $vector_length) {
            die ("Vector dimensions do not match. : $!");
        }
    }
    for (my $i = 0; $i <= $#vectors; $i++) {
        $sum{$.}{$i} += $vectors[$i];
    }
    $line_number = $.;
    $. = 0 if eof;
}

$" = "\t\t";
for (my $line=1; $line<=$line_number; $line++)
{
    print $line;
    for (my $vector=0; $vector<$vector_length; $vector++)
    {
        print " " . $sum{$line}{$vector};
    }
    print "\n";
}

【讨论】:

  • “试试这个”没有进一步解释的答案是相当烦人的,而且不是很有价值。这意味着我必须通读您的整个代码,看看它与 OP 有何不同,并尝试辨别您要做什么,以及您是否做对了。
  • close(ARGV) if eof; 是记录的示例。
  • 如果最后一行是空白或注释则不起作用。
猜你喜欢
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 2022-10-01
  • 1970-01-01
  • 2015-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多