拥有这个片段:
my $file = "input.txt"; # let's assume that this is an ascii file
my $size1 = -s $file;
print "$size1n";
$size2 = 0;
open F, $file;
$size2 += length($_) while (<F>);
close F;
print "$size2n";
什么时候可以断言$size1
等于$size2
是真的?
如果您没有指定支持多字节字符的编码,那么它应该是有效的。否则,结果可能会有所不同:
$ cat 1.txt
žluťoučký kůň
$ perl -E 'say -s "1.txt";
open my $FH, "<:utf8", "1.txt";
my $f = do { local $/; <$FH> };
say length $f;'
20
14
您不能,因为输入层可能会对输入行进行一些转换,例如将crlf
更改为cr
,这可能会更改该行的长度。
此外,length $line
计算$line
中的字符数,在多字节编码中,如@choroba给出的示例,一个字符可能占用多个字节。
详见perlio
。
不,正如Lee Duhem
所说,这两个数字可能不同,因为Perl的行末处理,或者因为length
以字符报告字符串的大小,如果文本中有任何宽字符,就会抛出数字。
然而,tell
函数将以字节为单位报告您已读取的精确位置,因此与您的程序等价的是,该保证的数字匹配
use strict;
use warnings;
my $file = 'input.txt';
my $size1 = -s $file;
print "$size1n";
open my $fh, '<', $file or die $!;
my $size2 = 0;
while (<$fh>) {
$size2 = tell $fh;
}
close $fh;
print "$size2n";
请注意use strict
和use warnings
的使用、词法文件句柄、open
的三参数形式以及是否成功的检查。所有这些都是Perl程序的最佳实践,应该在编写
您只是缺少binmode(F);
或:raw
IO层。这些导致Perl返回的文件与磁盘上显示的文件完全一样。没有行尾翻译。没有对字符编码进行解码。
open(my $fh, '<:raw', $file)
or die "open $file: $!n");
然后你的代码就可以正常工作了。
my $size = 0;
$size += length while <$fh>;
这不是特别好,因为对于二进制文件,它可以一次读取整个文件。因此,让我们改为读取固定大小的块。
local $/ = (64*1024);
my $size = 0;
$size += length while <$fh>;
这基本上与使用read
相同,后者一次读取4K或8K(以较新的Perls)。一次阅读更多内容会带来性能优势,我们可以使用sysread
来做到这一点。
my $size = 0;
while (my $bytes_read = sysread($fh, my $buf, 64*1024)) {
$size += $bytes_read;
}
不过,阅读整个文件是愚蠢的。你可以直接找到文件的末尾。
use Fcntl qw( SEEK_END );
my $size = sysseek($fh, 0, SEEK_END);
但话说回来,您还不如直接使用-s
。
my $size = -s $fh;