-s和我的实现之间的区别



拥有这个片段:

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 strictuse 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;

相关内容

  • 没有找到相关文章

最新更新