使用Perl计算消息中字符的频率

4
我正在编写一个Perl脚本,用于查找消息中字符出现频率。以下是我遵循的逻辑:
- 使用getc()从消息中逐个读取字符,并将其存储到数组中。 - 运行一个for循环,从索引0到该数组的长度。 - 此循环将读取数组的每个字符并将其分配给临时变量。 - 运行另一个嵌套在上面的for循环,该循环将从正在测试的字符的索引运行到数组的长度。 - 通过字符串比较此字符和当前数组索引字符之间的差异,如果它们相等,则增加计数器。 - 完成内部For循环后,我会打印char的频率以进行调试。
问题:如果已经计算了字符的频率,则不希望程序重新计算它。例如,如果字符“a”出现3次,则对于第一次运行,它计算出正确的频率。但是,在下一个“a”的出现中,由于循环从该索引运行到结尾,因此频率为(实际频率-1)。对于第三次出现,频率为(实际频率-2)。
为解决这个问题,我使用了另一个临时数组来保存已经评估过频率的char。
然后,在下一次for循环运行之前,我将当前char与已评估char的数组进行比较并设置标志。基于该标志,运行内部for循环。
但是这对我没有起作用,结果仍然相同。
以下是我编写的代码以实现上述功能:
#!/usr/bin/perl

use strict;
use warnings;

my $input=$ARGV[0];
my ($c,$ch,$flag,$s,@arr,@temp);

open(INPUT,"<$input");

while(defined($c = getc(INPUT)))
{
push(@arr,$c);
}

close(INPUT);

my $length=$#arr+1;

for(my $i=0;$i<$length;$i++)
{
$count=0;
$flag=0;
$ch=$arr[$i];
foreach $s (@temp)
{
    if($ch eq $s)
    {
        $flag = 1;
    }
}
if($flag == 0)
{
for(my $k=$i;$k<$length;$k++)
{
    if($ch eq $arr[$k])
    {
        $count = $count+1;
    }
}
push(@temp,$ch);
print "The character \"".$ch."\" appears ".$count." number of times in the         message"."\n";
}
}
5个回答

4
你正在让生活变得比必要的更加困难。使用哈希:
my %freq;

while(defined($c = getc(INPUT)))
{
  $freq{$c}++;
}

print $_, " ", $freq{$_}, "\n" for sort keys %freq;

$freq{$c}++会增加$freq{$c}中存储的值。(如果它未设置或为零,则变为1。)

打印行等同于:

foreach my $key (sort keys %freq) {
  print $key, " ", $freq{$key}, "\n";
}

Mat的方法比我的解决方案更高效,而且更简单!+1 - Drav Sloan
谢谢您提供的解决方案。现在,我清楚了Perl中哈希的概念以及如何使用它们。 - Neon Flash

3
如果你想对整个文件进行单个字符计数,请使用其他人发布的任何建议方法。如果你想要在文件中计算每个字符的所有出现次数,则我建议:
#!/usr/bin/perl

use strict;
use warnings;

# read in the contents of the file
my $contents;
open(TMP, "<$ARGV[0]") or die ("Failed to open $ARGV[0]: $!");
{
    local($/) = undef;
    $contents = <TMP>;
}
close(TMP);

# split the contents around each character
my @bits = split(//, $contents);

# build the hash of each character with it's respective count
my %counts = map { 
    # use lc($_) to make the search case-insensitive
    my $foo = $_; 

    # filter out newlines
    $_ ne "\n" ? 
        ($foo => scalar grep {$_ eq $foo} @bits) :
        () } @bits;

# reverse sort (highest first) the hash values and print
foreach(reverse sort {$counts{$a} <=> $counts{$b}} keys %counts) {
    print "$_: $counts{$_}\n";
}

记录一下,lc 只能让 ASCII 不区分大小写,不能处理 Unicode。 - tchrist
太棒了!这个解决方案非常高效。感谢您提供grep和map函数使用的好例子。这让我更加喜欢grep :) - Neon Flash

2
我不明白您试图解决的问题,因此我提出了一种更简单的方法来计算字符串中的字符数:
$string = "fooooooobar";
$char = 'o';
$count = grep {$_ eq $char} split //, $string;
print $count, "\n";

这将打印出 $string 中 $char 出现的次数 (7)。希望这能帮助您编写更简洁的代码。

2
作为一行代码:
perl -F"" -anE '$h{$_}++ for @F; END { say "$_ : $h{$_}" for keys %h }' foo.txt

1
更快的解决方案:
@result = $subject =~ m/a/g; #subject is your file

print "Found : ", scalar @result, " a characters in file!\n";

当然,你可以将一个变量放在'a'的位置,甚至更好的是,对于任何你想要计算出现次数的字符执行这行代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接