【问题标题】:Count the occurrences of a string in a file计算文件中字符串的出现次数
【发布时间】:2012-08-15 22:23:23
【问题描述】:

我编写了一个 perl 脚本来计算文件中字符的出现次数。 到目前为止,这就是我所拥有的,

#!/usr/bin/perl -w

use warnings;
no warnings ('uninitialized', 'substr');

my $lines_ref;
my @lines;
my $count;

sub countModule()
{
my $file = "/test";
open my $fh, "<",$file or die "could not open $file: $!";
my @contents = $fh;
my @filtered = grep (/\// ,@contents);
return \@filtered;
}

@lines = countModule();
#@lines = $lines_ref;
$count = @lines;
print "###########\n $count \n###########\n";

我的测试文件如下所示:

10.0.0.1/24
192.168.10.0/24
172.16.30.1/24

我基本上是在尝试计算“/”的实例数

这是我得到的输出:

###########
 1
###########

我得到的是 1 而不是 3,这是出现次数。 仍在学习 perl,所以任何帮助将不胜感激..谢谢!

【问题讨论】:

标签: perl


【解决方案1】:

这里有几点关于你的代码

  • 您应该始终在程序顶部使用use strict,并且仅在有限范围内出于特殊原因使用no warnings。没有一般的理由说明一个正常的 Perl 程序需要全局禁用警告

  • 将变量声明为接近它们的第一个使用点。在文件顶部声明所有内容的风格是不必要的,是 C 的遗产

  • 从不在您的代码中使用原型。它们可用于非常特殊的目的,不应用于绝大多数 Perl 代码。 sub countModule() { ... } 坚持认为 countModule 可能永远不会使用任何参数调用,并且没有必要或有用。定义应该只是sub countModule { ... }

  • 干得好!使用词法文件句柄,open 的三参数形式,并将$! 放入你的骰子字符串

  • my @contents = $fh 只会将@contents 设置为仅包含文件句柄的单元素列表。要将整个文件读入数组,您需要my @contents = &lt;$fh&gt;

  • 如果您使用不同的分隔符,您可以避免在正则表达式中转义斜杠。为此,您需要明确使用 m 运算符,例如 my @filtered = grep m|/|, @contents)

  • 您返回一个数组引用,但将返回的值分配给一个数组,因此@lines = countModule()@lines 设置为仅包含数组引用的单元素列表。您应该使用return @filtered 返回一个列表,或者使用@lines = @{ countModule } 取消引用赋值时的返回值

如果您只需要打印文件中包含斜杠字符的行数,那么您可以编写类似这样的内容

use strict;
use warnings;

my $count;

sub countModule {
  open my $fh, '<', '/test' or die "Could not open $file: $!";
  return [ grep m|/|, <$fh> ];
}

my $lines = countModule;
$count = @$lines;
print "###########\n $count \n###########\n";

【讨论】:

  • 非常感谢您的详细分析..非常有帮助...抱歉没有让我的问题有点含糊...我想计算字符的出现次数而不是行数..谢谢!
【解决方案2】:

关闭,但有一些问题:

use strict;
use warnings;

sub countModule
{
    my $file = "/test";
    open my $fh, "<",$file or die "could not open $file: $!";
    my @contents = <$fh>;  # The <> brackets are used to read from $fh.
    my @filtered = grep (/\// ,@contents);
    return @filtered;      # Remove the reference.
}

my @lines = countModule();
my $count = scalar @lines;  # 'scalar' is not required, but lends clarity.
print "###########\n $count \n###########\n";

我对您的代码所做的每一项更改都带有#comment 注释,解释所做的工作。

现在在列表上下文中,您的子例程将返回过滤后的行。在标量上下文中,它将返回过滤的行数。

您确实还提到了查找字符的出现(尽管脚本中的所有内容都是面向行的)。也许您的计数器子会如下所示:

sub file_tallies{
    my $file = '/test';
    open my $fh, '<', $file or die $!;
    my $count;
    my $lines;
    while( <$fh> ) {
        $lines++;
        $count += $_ =~ tr[\/][\/];
    }
    return ( $lines, $count );
}

my( $line_count, $slash_count ) = file_tallies();

【讨论】:

  • 这会计算带有斜线的行数,而不是斜线的数量。这可能没问题,规格尚不清楚。
  • 我添加了一个更新,该更新同时计算了行数和“斜线”计数,以防他想要这样做。
  • @DavidO:为什么没有use strictno warnings 是干什么用的?你永远不会使用$lines_ref@lines$count 应该在它们被分配的地方声明。要让人们编写可敬的 Perl 却没有显示相同错误的可靠答案已经够难的了
  • @Borodin 天哪,我怎么没看到?我试图在 OP 的代码中尽可能少地更改,以准确说明使其工作所需的内容。我现在已经删除了。你是绝对正确的。感谢您的推动。
  • @DavidO:值得注意的是,对tr/// 使用非标准分隔符允许您在搜索列表中使用斜杠而不转义它。如果没有指定字符串,默认情况下$_ 绑定到tr///。所以$count += tr[/][] 很好。
【解决方案3】:

在列表上下文中,

return \@filtered;

返回一个包含一个元素的列表——对命名数组@filtered 的引用。也许您想返回列表本身

return @filtered;

【讨论】:

  • 当我尝试您的编辑时,我的打印语句返回“0”...我尝试返回出现的“/”...有没有更好的方法来做到这一点??
【解决方案4】:

这里有一些更简单的代码:

sub countMatches {
    my ($file, $c) = @_;   # Pass parameters
    local $/;
    undef $/; # Slurp input
    open my $fh, "<",$file or die "could not open $file: $!";
    my $s = <$fh>;  # The <> brackets are used to read from $fh.
    close $fh;
    my $ptn = quotemeta($c);  # So we can match strings like ".*" verbatim
    my @hits = $s =~ m/($ptn)/g;
    0 + @hits
}

print countMatches ("/test", '/') . "\n";

代码将 Perl 推到了非常基础的范围之外,但并不过分。重点:

  1. 通过取消 $/ 可以将输入读入一个字符串。如果你在数 文件中字符串的出现次数,而不是包含的行的出现次数 字符串,这通常更容易做到。

  2. m/(...)/g 会找到所有的命中,但如果你想计算像这样的字符串 “。”您需要引用其中的元字符。

  3. 将结果存储在数组中以在列表上下文中评估 m//

  4. 将 0 添加到列表中会给出其中的项目数。

【讨论】:

  • local $/$/ 的本地值设置为未定义;也没有理由undef 它。你没有理由quotemeta $c 模式; my @hits = $s =~ m/\Q$c/g 很好。返回@hits 大小的正确方法是return scalar @hits。如果您对匹配的实际字符串不感兴趣,则不应使用捕获:只需 my $n = () = $s =~ m/\Q$c/g; return $n 即可正常工作
猜你喜欢
  • 2016-04-28
  • 1970-01-01
  • 2016-08-22
  • 2011-10-08
  • 1970-01-01
  • 2014-04-24
  • 2013-12-23
相关资源
最近更新 更多