我在使用Perl从文本文件读取的字符串列表的频率计算方面遇到了问题。我想要做的是,分别计算每一行中的字符数。我的代码可以正常工作,但它也将原始字符串计算为单个字符。
这是我的代码,
sub cal_frequency{
while (my @row = <$fd>){
chomp;
for (my $i=0; $i<=147; $i++){
my %count;
print "$row[$i]\n";
foreach my $str (split //, $row[$i]) {
$count{$str}++;
}
foreach my $str (sort keys %count) {
printf "%-31s %s\n", $str, $count{$str} ;
}
}
}
}
这是我得到的输出。
HMHHMSHHHNHHMEHFFHHHHHHHDHDE
1
D 2
E 2
F 2
H 17
M 3
N 1
S 1
LLLWLFLLWLWLLWWLLLLFLLLLWLFW
1
F 3
L 18
W 7
TTTTTDTTTTTTTTTTSTTTTSSTTATT
1
A 1
D 1
...
...
...
有没有人可以帮我弄清楚代码有什么问题。
答案 0 :(得分:8)
你正在咀嚼错误的变量。您正在阅读@row
的输入,但是您只需在没有参数的情况下调用chomp;
。这会阻止$_
,而不是@row
。
while (my @row = <$fd>){
chomp @row;
# ...
你可能已经找到了自己。如果您为每个输入行输出%count
的实际内容,您会看到有一个新行\n
被计算一次。所以你看到的第一行是
HMHHMSHHHNHHMEHFFHHHHHHHDHDE 1
实际上不是完整的字符串。它是字符串末尾的换行符。试试这个。我建议你将你的迭代变量改为只做一行,这样你就不会被输出所淹没。
for ( my $i = 0; $i <= 1; $i++ ) {
# ...
foreach my $str ( sort keys %count ) {
printf "%-31s %s\n", $str, $count{$str};
}
use Data::Dumper;
print Dumper \%count;
}
您现在可以看到:
$VAR1 = {
'A' => 1,
'S' => 3,
'T' => 23,
'
' => 1,
还有线路。
你的代码有点奇怪。你做了很多不必要的事情。我将尝试为您解释它们以简化您的代码。
您的while
循环只会运行一次,因为您正在将整个文件一次性压入@row
。
sub cal_frequency {
while ( my @row = <DATA> ) {
print "while iteration\n";
我从输出中省略了printf
。
while iteration
HMHHMSHHHNHHMEHFFHHHHHHHDHDE
LLLWLFLLWLWLLWWLLLLFLLLLWLFW
TTTTTDTTTTTTTTTTSTTTTSSTTATT
如您所见,迭代时只有一个 。如果你有一个很长的文件,那么逐行读取它会更聪明。
while ( my $row = <DATA> ) {
chomp $row;
# ...
}
一旦这样做,您的C风格for
循环就会过时。实际上,该循环将程序耦合到输入,因为您在那里有硬编码的输入行数。如果没有这个循环,程序可以处理任意长的文件(比如你向我们展示的三条线和我正在使用的文件)。
尽可能晚地声明变量并尽可能接近您需要的变量也是一种很好的风格。我已经移动了my %count
并更改了缩进。
sub cal_frequency {
while ( my $row = <DATA> ) {
chomp $row;
print "$row\n";
my %count;
foreach my $str ( split //, $row ) {
$count{$str}++;
}
foreach my $str ( sort keys %count ) {
printf "%-31s %s\n", $str, $count{$str};
}
}
}
现在更清楚的是发生了什么。
在您的代码中,您使用的是词法文件句柄。那很棒。但它似乎是在子程序之外创建的。把它作为一个论点传递会更好。
最后,在两个$str
循环中选择变量名foreach
会让人感到困惑。当我看到 string 时,我会想到很长的单词或其他东西。但是在这里你特别希望将你的字符串(行)分成字符。这是长度为1的字符串。将它们命名为字符。
这是最终的节目。
use strict;
use warnings;
sub cal_frequency {
my ($fh) = @_;
while ( my $row = <$fh> ) {
chomp $row;
print "$row\n";
my %count;
foreach my $chr ( split //, $row ) {
$count{$chr}++;
}
foreach my $chr ( sort keys %count ) {
printf "%-31s %s\n", $chr, $count{$chr};
}
}
}
cal_frequency(\*DATA);
__DATA__
HMHHMSHHHNHHMEHFFHHHHHHHDHDE
LLLWLFLLWLWLLWWLLLLFLLLLWLFW
TTTTTDTTTTTTTTTTSTTTTSSTTATT
输出。
HMHHMSHHHNHHMEHFFHHHHHHHDHDE
D 2
E 2
F 2
H 17
M 3
N 1
S 1
LLLWLFLLWLWLLWWLLLLFLLLLWLFW
F 3
L 18
W 7
TTTTTDTTTTTTTTTTSTTTTSSTTATT
A 1
D 1
S 3
T 23
答案 1 :(得分:0)
它也将原始字符串计算为单个字符。
如果你在讨论输出中的行,比如
HMHHMSHHHNHHMEHFFHHHHHHHDHDE
那是因为你用
打印每一行print "$row[$i]\n"
以下是我如何编码您的问题。特别是我将文件名传递给子例程,并使用全局正则表达式模式提取列表中的所有非空格字符,以便{/ 1}}不必要
我想知道你是否真的想要输入中每一行的频率转储,或者是否应该将所有行合在一起。此代码将您的代码分别视为每行。我在每个输出块之间打印了一个分隔线,使它们不同
chomp
use strict;
use warnings 'all';
cal_frequency('cal_freq.txt');
sub cal_frequency {
my ($file) = @_;
open my $fh, '<', $file or die qq{Unable to open "$file" for input: $!};
while ( <$fh> ) {
my %count;
++$count{$_} for /\S/g;
printf "%-2s %s\n", $_, $count{$_} for sort keys %count;
print "---\n";
}
}