我正在编写一个脚本来获取文件aryData
中的整数列表,对它们进行排序,打印排序的数组,最高值和最低值。
89 62 11 75 8 33 95 4
但是,当打印最高或最低值时,将打印阵列的所有元素。
这是我的Perl代码
#!/bin/perl
use strict;
use warnings;
print "Enter filename to be sorted: ";
my $filename = <STDIN>;
chomp( $filename );
open( INFILE, "<$filename" );
my @nums = <INFILE>;
close INFILE;
my @sorted = sort { $a cmp $b } @nums;
open my $outfile, '>', "HighLow.txt";
print $outfile "Sorted numbers: @sorted";
print $outfile "Smallest number: $sorted[0] \n";
print $outfile "Largest number: $sorted[-1] \n";
HighLow.txt
Sorted numbers: 89 62 11 75 8 33 95 4
Smallest number: 89 62 11 75 8 33 95 4
Largest number: 89 62 11 75 8 33 95 4
答案 0 :(得分:5)
这个答案将有很大一部分代码审查和解释与问题没有直接关系的概念。
让我们看一下读入数组的代码部分。
open(INFILE, "<$filename");
my @nums = <INFILE>;
close INFILE;
此代码适用于您正在执行的操作,但它有一些安全性和样式问题,我将在下面进一步介绍。
所以你有一个文件名,你逐行阅读文件。每一行都进入数组@nums
中的一个元素。由于你的东西没有按你想要的方式工作,你需要调试的第一步是尝试查看数组。
你这样做的尝试并不是一个坏主意。
print "Sorted numbers: @sorted";
Interpolating Perl中双引号""
字符串中的数组使用变量$,
连接数组的元素,也称为output field separator。默认情况下,它是一个空格。
my @foo = (1, 2, 3);
print "@foo";
这将提供以下输出
1 2 3
不幸的是,您的输入文件已经有空格作为分隔符,并且所有数字都在一行上。所以你无法真正看到阵列没有正确设置。这是你自己注意到的那些面孔之一。您可以通过查看已排序的数字注意到它。你对它们进行了排序,但它们没有排序。
Sorted numbers: 89 62 11 75 8 33 95 4
更好的方法是弄清楚数组中的内容是使用Data::Dumper,它可以让您serialize数据结构。它包含在Perl中。
use Data::Dumper;
my @foo = (1, 2, 3);
print Dumper \@foo;
该模块为您提供Dumper
功能。它喜欢在引用上更好地工作,因此您需要添加反斜杠以创建对@foo
的引用。在这一点上,这意味着什么并不重要。请记住,如果您的变量没有$
,则在前面添加一个反斜杠。
$VAR1 = [
1,
2,
3
];
这很有用。它告诉我们三个要素。现在让我们来看看你的代码。我使用的是伪文件句柄DATA
,而不是实际的文件,它从程序末尾的__DATA__
部分读取。这非常适合测试和示例。
use Data::Dumper;
my @nums = <DATA>;
my @sorted = sort { $a cmp $b } @nums;
print Dumper \@sorted;
__DATA__
89 62 11 75 8 33 95 4
打印
$VAR1 = [
'89 62 11 75 8 33 95 4
'
];
我们在这里可以看到两件事。首先,所有数字都在一行上,因此它们进入第一个元素。其次,该行最后有一个换行符。您已经知道可以使用chomp
删除它。
所以我们试着解决这个问题。我们现在知道我们需要split
数字线。 There are many different ways完成此任务。我会用一个非常详细的解释所涉及的步骤。
use Data::Dumper;
my $line = <DATA>; # only read one line
chomp $line; # remove the line ending
my @nums = split / /, $line;
my @sorted = sort { $a cmp $b } @nums;
print Dumper \@sorted;
__DATA__
89 62 11 75 8 33 95 4
我们将split
与空模式 / /
一起使用,将数字字符串转换为数字列表,并将其放入数组中。然后我们排序。
$VAR1 = [
'11',
'33',
'4',
'62',
'75',
'8',
'89',
'95'
];
如您所见,我们现在有一个排序的数字列表。但它们没有按数字排序。相反,它们是sorted asciibetically。这是因为cmp
is the operator按ASCII字符编号排序。它也是Perl sort
的默认行为,因此您可以省略整个{ $a cmp $b }
块。它只是说sort @nums
。
但我们希望按数字值对数字进行排序,因此我们需要使用<=>
排序运算符。
use Data::Dumper;
my $line = <DATA>; # only read one line
chomp $line; # remove the line ending
my @nums = split / /, $line;
my @sorted = sort { $a <=> $b } @nums;
print Dumper \@sorted;
__DATA__
89 62 11 75 8 33 95 4
现在程序打印出正确的输出。
$VAR1 = [
'4',
'8',
'11',
'33',
'62',
'75',
'89',
'95'
];
我会留给你把它放回你的实际程序中。
最后,关于你open
的一句话。您正在使用名为 glob filehandles 的内容。像INFILE
这样的东西是全局标识符。它们在整个程序中都有效,即使在您可能加载的其他模块中也是如此。虽然在这个微不足道的计划中并没有真正发挥作用,但未来可能会引发问题。例如,如果Data :: Dumper模块要打开一个文件并使用相同的标识符INFILE
,并且您没有调用close INFILE
,那么您的程序可能会崩溃或做非常奇怪的事情,因为它会重用相同的句柄。
相反,您可以使用词汇文件句柄。词法变量仅在某个范围内有效,如函数或循环体。它只是一个常规变量,用my
声明。当超出范围时,它会自动为您调用close
。
open my $fh, "<foo";
my @nums = <$fh>;
close $fh;
您正在使用两个参数调用open
。这也不是一个好主意。现在你有<
模式,但是如果你把它遗漏掉并open my $fh, "$file"
并从用户那里读$file
,他们可能会传递一些不好的东西,比如| rm -rf slash
。然后Perl会将管道|
视为模式,打开管道并删除所有内容。相反,使用三参数open。
open my $fh, '<', 'foo';
现在你明确设置了模式,你就安全了。
最后一点是,您应该始终检查open
是否有效。这很容易。
open my $fh, '<', 'foo' or die $!;
variable $!
包含open
遇到的错误。仅当or
调用的返回值为false时,open
才会触发。 die
使程序终止。您可能收到的错误可能如下所示。
/home/foo/code/scratch.pl第6154行没有这样的文件或目录。
所以完整的文件阅读应该是这样的。
open my $fh, '<', $filename or die "Could not read $filename: $!";
my @nums = <$fh>;
close $fh;
答案 1 :(得分:2)
正如您从评论中看到的那样,问题在于您没有正确填充数组。您最终只有@nums
中的一个元素 - 它是包含所有数据的单个元素。
您可以通过使用类似Data::Dumper
的内容来确认,呃......转储您的数据: - )
在程序的顶部,在use warnings;
之后,您可以添加此内容:
use Data::Dumper;
然后在您加载@nums
之后,尝试转储它:
print Dumper(\@nums), "\n";
你会看到这个:
$VAR1 = [
'89 62 11 75 8 33 95 4
'
];
将其与我们解决问题时的情况进行比较,您会看到明显的差异。
因此,我们有一行数据,其中包含您对空格分隔的数字。要将其转换为我们可以存储在数组中的数字列表,我们可以使用split()
函数。 split()
有两个参数 - 一个用于拆分字符串的正则表达式和一个要拆分的字符串。
您可以使用此代码从文件中读取并分配给您的数组:
my @nums = <INFILE>;
您可以将其替换为:
my @nums = split / /, <INFILE>;
现在我们的数据转储如下所示:
$VAR1 = [
'89',
'62',
'11',
'75',
'8',
'33',
'95',
'4
'
];
我希望差异很明显。你的程序基本上可以在这一点上运行,但我们可以通过处理文件中记录末尾的换行来清理一下(你可以在上面4
之后看到它。)
我们需要将这条线分成两条。
chomp(my $input = <INFILE>);
my @nums = split / /, $input;
现在我们的数据转储如下所示:
$VAR1 = [
'89',
'62',
'11',
'75',
'8',
'33',
'95',
'4'
];
此时,您的程序仍然存在错误。我打算离开你进行调查(提示:sort()
实际上做了什么?Read the documentation) - 如果您遇到更多问题,请提出另一个问题。
但我想通过建议对您的一般编码风格进行一些改进来完成。我不确定你从哪里学习Perl,但是你正在做的一些事情看起来很陈旧。
当您在Perl中打开文件时,应始终检查打开调用的结果,并在失败时采取适当的操作。在许多情况下,杀死程序是适当的操作,因此我在您的公开声明中使用die()
。
open( INFILE, "<$filename" )
or die "Can't open $filename: $!\n");
错误消息中的$!
会告诉您为什么Perl无法打开该文件。
这些天也被视为最佳做法,以避免&#34;裸字文件句&#34; (与INFILE
一样),并从模式指示符(>
或<
)中拆分文件名。将所有这些放在一起,您的文件处理代码变为:
open( my $in_fh, '<', $filename )
or die "Can't open $filename: $!\n";
chomp(my $input = <$in_fh>);
my @nums = split / /, $input;
close $in_fh;
我发现你已经在输出文件中使用了这种风格。在同一个程序中混合样式似乎很奇怪。
答案 2 :(得分:1)
也许您可以尝试这样找出最大值和最小值:
@a=qw(1 3 2 8 7 5 4 10 9);
@a=sort {$a<=>$b}@a;
print "the max number=$a[0]\nthe min number=$a[$#a]\n";