【问题标题】:Calculate Character Frequency in Message using Perl使用 Perl 计算消息中的字符频率
【发布时间】:2011-10-16 12:58:48
【问题描述】:

我正在编写一个 Perl 脚本来找出消息中字符出现的频率。这是我遵循的逻辑:

  • 使用 getc() 从消息中一次读取一个字符并将其存储到数组中。
  • 从索引 0 开始到该数组的长度运行一个 for 循环。
  • 此循环将读取数组的每个字符并将其分配给一个临时变量。
  • 运行另一个嵌套在上面的 for 循环,它将从被测试字符的索引开始运行,直到数组的长度。
  • 使用此字符与当前数组索引字符之间的字符串比较,如果它们相等,则计数器会增加。
  • 内部 For 循环完成后,我正在打印 char 的频率以进行调试。

问题:我不希望程序重新计算已经计算过的字符的频率。例如,如果字符“a”出现 3 次,那么对于第一次运行,它会计算正确的频率。但是,在下一次出现“a”时,由于循环从该索引运行到结束,因此频率为(实际频率 -1)。同样,第三次出现的频率是(实际频率 -2)。

解决这个问题。我使用了另一个临时数组,我会将已评估频率的字符推送到该数组。

然后在 for 循环的下一次运行中,在进入内部 for 循环之前,我将当前字符与评估字符数组进行比较并设置一个标志。基于该标志,内部 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";
}
}

【问题讨论】:

    标签: perl frequency-analysis getc


    【解决方案1】:

    你让你的生活变得比需要的更艰难。使用哈希:

    my %freq;
    
    while(defined($c = getc(INPUT)))
    {
      $freq{$c}++;
    }
    
    print $_, " ", $freq{$_}, "\n" for sort keys %freq;
    

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

    打印行相当于:

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

    【讨论】:

    • Mat 的方法比我的解决方案更高效、更简单! +1
    • 感谢您的解决方案。现在,我对 Perl 中的哈希概念以及如何使用它们已经很清楚了。
    【解决方案2】:

    如果您想对整个文件进行单个字符计数,请使用其他人发布的任何建议方法。如果您想要计数所有的发生 然后我建议文件中的每个字符:

    #!/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。
    • 惊人的解决方案!它是如此高效。感谢您提供了一个很好的例子来说明 grep 和 map 函数的用法。这让我又多了一个喜欢 grep 的理由 :)
    【解决方案3】:

    我不明白您要解决的问题,因此我提出了一种更简单的方法来计算字符串中的字符数:

    $string = "fooooooobar";
    $char = 'o';
    $count = grep {$_ eq $char} split //, $string;
    print $count, "\n";
    

    这会在 $string (7) 中打印 $char 出现的次数。 希望这有助于编写更紧凑的代码

    【讨论】:

      【解决方案4】:

      更快的解决方案:

      @result = $subject =~ m/a/g; #subject is your file
      
      print "Found : ", scalar @result, " a characters in file!\n";
      

      当然,您可以将变量放在“a”的位置,或者更好地执行此行来计算您想要计算出现次数的任何字符。

      【讨论】:

        【解决方案5】:

        作为单行:

        perl -F"" -anE '$h{$_}++ for @F; END { say "$_ : $h{$_}" for keys %h }' foo.txt
        

        【讨论】:

          猜你喜欢
          • 2018-04-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-18
          • 2017-04-19
          • 1970-01-01
          相关资源
          最近更新 更多