首页 > 解决方案 > 在 Perl 中转到我们想要的任何行号的最佳方法

问题描述

举个例子,我正在阅读这个文件,在一些单词之后我检查有多少“你”存在。

Good 
morning
to
you
May
every
step
you
make
you
be
filled
you
with
happiness
love
you
and
peace
you

我写的代码:

use warnings;
use strict;

my $log1_file      = "log.log";
my $you_count      = 0;
my $you_make_count = 0;
my $you_love_count = 0;
my $point ;

open(IN1, "<$log1_file" ) or die "Could not open file $log1_file: $!";
while (my $line = <IN1>) {
    $point =$.;
    print "$. main while\n";
    my @fields = split' ',$line;
   
    if ($fields[0] eq "Good") {
        print "$. after good_if\n";
        good_check();
        print "$. after good_call\n";
        seek (IN1,$point,0);
        #$. = $point;
        print "$. \n";
    }  
    elsif ($fields[0] eq  "make") {
        print "$. after make_if\n";
        make_check();
        #$. = $point;
        seek (IN1,$point,0);
    }
    elsif ($fields[0] eq  "love") {
        print "$. after love_if\n";
        love_check();
        #$. = $point;
        seek (IN1,$point,0);
    }
}

print "$you_count\n";
print "$you_make_count\n";
print "$you_love_count\n";
close IN1;
   
sub love_check{
    while (my $line = <IN1>) 
        my @fields = split' ',$line;
        if ($fields[0] eq "you") {
            $you_love_count++;
        }
    }
}       
    
sub make_check{
    while (my $line = <IN1>) {   
        my @fields = split' ',$line;        
        if ($fields[0] eq "you") {
            $you_make_count++;
        }
    }
}
 
sub good_check{
    while (my $line = <IN1>) {
        my @fields = split' ',$line;
        if ($fields[0] eq "you") {
            $you_count++;
        }
    }
}

如果我用seek (IN1,$point,0);回指向位置,我会得到如下输出:

1 main while
1 after good_if
20 after good_call
20 
21 main while
22 main while
23 main while
24 main while
25 main while
26 main while
27 main while
28 main while
29 main while
29 after make_if
41 main while
42 main while
43 main while
44 main while
44 after make_if
56 main while
Use of uninitialized value $fields[0] in string eq at check.pl line 15, <IN1> line 56.
Use of uninitialized value $fields[0] in string eq at check.pl line 25, <IN1> line 56.
Use of uninitialized value $fields[0] in string eq at check.pl line 33, <IN1> line 56.
57 main while
58 main while
59 main while
60 main while
61 main while
62 main while
63 main while
63 after love_if
68 main while
69 main while
70 main while
70 after love_if
75 main while
76 main while
76 after love_if
81 main while
82 main while
82 after love_if
87 main while
Use of uninitialized value $fields[0] in string eq at check.pl line 15, <IN1> line 87.
Use of uninitialized value $fields[0] in string eq at check.pl line 25, <IN1> line 87.
Use of uninitialized value $fields[0] in string eq at check.pl line 33, <IN1> line 87.
88 main while
89 main while
90 main while
91 main while
6
8
8

final 的“you”值是正确的,但没有正常获取行号。如果我$. = $point;只使用第一个子工作正常。

谁能告诉我指向同一位置的最佳方法?

标签: functionperl

解决方案


这个问题看起来很像一个XY 问题。或者家庭作业。计算“你”与某些关键词相关的逻辑似乎是任意的。例如,“Good”之后的“you”将包含其他单词中的所有“you”组合。

由于我认为这是某种学习练习,我将评论您的代码,然后提供建议的解决方案。

open(IN1, "<$log1_file" ) or die "Could not open file $log1_file: $!";

始终使用带有显式打开模式的三个参数 open 以避免代码注入。使用词法文件句柄 ( my $fh) 而不是全局裸词 ( IN1)。它应该如下所示:

open my $fh, "<", $log1_file or die "Could not open '$log1_file': $!";

这种分裂是不必要的

my @fields = split' ',$line;

由于每一行只有一个单词,所有这一切都是删除最后的换行符(因为拆分' '是一种特殊情况)。如果要删除换行符,则应使用chomp,如下所示:chomp($line)

使用seektell浏览您的文件可能是错误的解决方案。虽然你可以让它工作,但有更好的解决方案。

IMO,使用三个几乎相同的子程序来做完全相同的事情(几乎)是不好的做法。在子例程中使用全局变量也不是一件好事。您应该寻求的是封装:将您需要的信息提供给子程序,然后返回您想要的值。例如:

my @file = <$fh>     # slurp the file into an array
....
if (/^Good$/) {
    $good_count += you_check($line_number);
} elsif (/^make$/) {
    $make_count += you_check($line_number);
} ....etc

sub you_check {
    my $line_number = shift;
    my $count = 0;
    for my $line ($file[$line_number] .. $file[$#file]) {
        $count++ if $line =~ /^you$/;
    }
    return $count;
}

假设我们保持@file不变,you_check()可以使用该函数而不必担心使用它会改变其他内容。

话虽如此,如果我要解决这个任务,我会使用哈希。它将允许您动态确定关键字,并添加新关键字而无需添加大量新代码。

use strict;
use warnings;
use Data::Dumper;

my %count;
my $key;
while (<>) {
    if (/(Good|make|love)/) {
        $key = $1;
    }
    if (/you/) {
        $count{$key}++ if $key;
    }
}
print Dumper \%count;

在命令行上像这样使用它:

$ count.pl log.log

与示例数据一起使用时的输出是:

$VAR1 = {
          'love' => 2,
          'Good' => 2,
          'make' => 2
        };

如果您仍想保持第一个单词计数包含所有其他单词的规则,您可以跟踪哪个单词先出现,然后再添加计数。对这个计数使用散列可以扩展为尽可能多的单词来跟踪。


推荐阅读