1

我的测试中有一个问题:

计算文件中的行数和单词数的程序有什么问题?

open F, $ARGV[0] || die $!;
my @lines = <F>;
my @words = map {split /\s/} @lines;
printf "%8d %8d\n", scalar(@lines), scalar(@words);
close(F); 

我的猜想是:

  1. 如果文件不存在,程序不会告诉我们。
  2. 如果文件中有标点符号,程序将计算它们,例如,在

    abc cba
    , , ,dce
    

    将是五个字,但另一方面wc输出相同的结果,因此它可能被认为是正确的行为。

  3. 如果F是一个大文件,最好遍历行而不是将其转储到lines数组中。

你有什么不那么琐碎的想法吗?

4

3 回答 3

5

在第一行,你有一个优先级问题:

open F, $ARGV[0] || die $!;

是相同的

open F, ($ARGV[0] || die $!);

这意味着die如果文件名为假,则执行,如果open失败则不执行。你想说

open(F, $ARGV[0]) || die $!;

或者

open F, $ARGV[0] or die $!;

此外,您应该使用 open 的 3 参数形式,以防$ARGV[0]包含对open.

open F, '<', $ARGV[0] or die $!;

另一方面,拆分/\s/意味着您在连续的空白字符之间得到一个“单词”。您可能的意思是/\s+/,或如 amphetamachine 所建议的/\W+/,取决于您要如何定义“单词”。

如果该行以空格开头,那么仍然会出现空“单词”的问题。您可以拆分' '以抑制它(这是一种特殊情况),或者您可以先修剪前导空格,或者插入 agrep { length $_ }以清除空的“单词”,或者放弃split并使用不同的方法来计算单词。

Processing line by line instead of reading the whole file at once would also be a good improvement, but it's not as important as those first two items.

于 2010-10-21T18:37:58.980 回答
3
  • 你的猜想#1 是不正确的:如果open失败,你的程序就会死掉。(请参阅 cjm 的回答重新操作顺序。)
  • 您使用的是全局文件句柄,而不是词法变量。
  • 您没有使用open.
  • 您可以只从标准输入中读取,这为输入提供了更大的灵活性——用户可以提供一个文件,或者将输入通过管道传输到标准输入中。
  • 最后,我不会编写自己的代码来解析单词;我会联系 CPAN,比如Lingua::EN::Splitter
use strict; use warnings;
use Lingua::EN::Splitter qw(words);
my ($wordcount, $lines);
while (<>)
{
    my $line = $_;
    $lines++;
    $wordcount += scalar(words $line);
}

printf "%8d %8d\n", $lines, $wordcount;
于 2010-10-21T18:04:44.130 回答
1

open F, $ARGV[0] || die $!如果文件不存在,您将有效退出。

这里有一些改进:

{local $/; $lines = <F>;} # read all lines at once

my @words = split /\W+/, $lines;
于 2010-10-21T18:02:36.287 回答