在Perl中检查LF是否位于大标量末尾的最快方法?
我想出了以下方法来检查换行符的$scaler的最终字符:在Perl中检查LF是否位于大标量末尾的最快方法?,perl,Perl,我想出了以下方法来检查换行符的$scaler的最终字符: if( $buffer !~ /\n$/ ) { if( substr( $buffer, -1, 1 ) !~ /\n/ ) { if( substr( $buffer, -1, 1 ) ne '\n' ) { 有没有更快的方法?$buffer标量的大小可能会变大,我注意到它越大,这些条件运行的时间就越长。我确实有另一个标量包含$buffer的长度,如果这有帮助的话 谢谢 完整代码: #!/usr/bin/perl use stric
if( $buffer !~ /\n$/ ) {
if( substr( $buffer, -1, 1 ) !~ /\n/ ) {
if( substr( $buffer, -1, 1 ) ne '\n' ) {
有没有更快的方法?$buffer标量的大小可能会变大,我注意到它越大,这些条件运行的时间就越长。我确实有另一个标量包含$buffer的长度,如果这有帮助的话
谢谢
完整代码:
#!/usr/bin/perl
use strict;
use warnings;
use Fcntl qw();
use Time::HiRes qw( gettimeofday tv_interval );
use constant BUFSIZE => 2 ** 21; # 2MB worked best for me, YMMV.
die "ERROR: Missing filename" if( !$ARGV[0] );
my $top = [gettimeofday];
sysopen( my $fh, $ARGV[0], Fcntl::O_RDONLY | Fcntl::O_BINARY ) or
die "ERROR: Unable to open $ARGV[0], because $!\n";
open my $output, ">", "/dev/null"; # for 'dummy' processing
my $size = -s $ARGV[0];
my $osiz = $size;
my( $buffer, $offset, $lnCtr ) = ( "", "", 0 );
while( $size ) {
my $read = sysread( $fh, $buffer, BUFSIZE, length($offset) );
$size -= $read;
my @lines = split /\n/, $buffer;
if( substr( $buffer, -1, 1 ) ne "\n" ) {
$offset = pop( @lines );
} else {
$offset = "";
}
for my $line ( @lines ) {
processLine( \$line );
$lnCtr++;
}
$buffer = $offset if( $offset );
}
close $fh;
print "Processed $lnCtr lines ($osiz bytes) in file: $ARGV[0] in ".
tv_interval( $top ).
" secs.\n";
print "Using a buffered read of ".BUFSIZE." bytes. - JLB\n";
sub processLine {
if( ref($_[0]) ) {
print $output ${$_[0]}."\n";
} else {
print $output $_[0]."\n";
}
return 0;
}
我想我已经达到了“收益递减点”,我试图让这个运行更快。它现在似乎能够以RAID5 SSD获取数据的速度读取数据。如您所见,我没有使用chomp()是有原因的,输入可以包含数十万个换行符,我需要保留这些换行符才能断开换行符进行处理。
./fastread.pl newdata.log
在0.674738秒内处理了文件newdata.log中的516670行(106642635字节)。
使用2097152字节的缓冲读取。-JLB我怀疑perl将字符串视为utf-8,出于某种原因,必须对整个字符串进行迭代 您可以暂时切换到字节语义,以查看末尾的字符是否为换行符
sub processLine {
print $_[0] $_[1];
}
open(my $fh, '<:raw', $ARGV[0])
or die("Can't open $ARGV[0]: $!\n");
my $buffer = '';
my $lnCtr = 0;
while (1) {
my $rv = sysread($fh, $buffer, BUFSIZE, length($buffer));
die $! if !defined($rv);
last if !$rv;
while ($buffer =~ s/(.*\n)//) {
processLine($1);
++$lnCtr;
}
}
if (length($buffer)) {
processLine($output, $buffer);
++$lnCtr;
}
请参阅文档了解Perl和。我怀疑Perl将字符串视为utf-8,并且出于某种原因必须对整个字符串进行迭代 您可以暂时切换到字节语义,以查看末尾的字符是否为换行符
sub processLine {
print $_[0] $_[1];
}
open(my $fh, '<:raw', $ARGV[0])
or die("Can't open $ARGV[0]: $!\n");
my $buffer = '';
my $lnCtr = 0;
while (1) {
my $rv = sysread($fh, $buffer, BUFSIZE, length($buffer));
die $! if !defined($rv);
last if !$rv;
while ($buffer =~ s/(.*\n)//) {
processLine($1);
++$lnCtr;
}
}
if (length($buffer)) {
processLine($output, $buffer);
++$lnCtr;
}
请参阅文档了解Perl和。您可以尝试。Chomp将返回从行尾删除的下线字符数:
if ( chomp $buffer ) {
print "You had an LF on the end of \$buffer";
}
当然,chomp会删除它计数的NL字符。您可以尝试。Chomp将返回从行尾删除的下线字符数:
if ( chomp $buffer ) {
print "You had an LF on the end of \$buffer";
}
当然,chomp会删除它计数的NL字符。你为什么担心速度?这段代码在您的程序中是不是有一部分相当慢,可能是用Devel::NYTProf描述的?如果没有,那么我建议你选择最清晰易读、最惯用的,可能是
if( $buffer !~ /\n$/ )
您的最终版本:
if( substr( $buffer, -1, 1 ) ne '\n' )
除了引用换行符之外,这也是一个不错的选择,这样可以得到一个由反斜杠和小写n组成的两个字符串。也许你来自C语言,其中单字符是单引号,字符串是双引号?你想要
if( substr( $buffer, -1, 1 ) ne "\n" )
这个版本
if( substr( $buffer, -1, 1 ) !~ /\n/ )
正在进行不应该的正则表达式匹配,因为它正在检查单字符字符串与单字符正则表达式。下一个阅读代码的人会认为这很奇怪,并想知道你为什么会这样做。另外,回到速度的问题上,将字符串与正则表达式进行匹配比仅与单个字符进行相等比较要慢。为什么要担心速度?这段代码在您的程序中是不是有一部分相当慢,可能是用Devel::NYTProf描述的?如果没有,那么我建议你选择最清晰易读、最惯用的,可能是
if( $buffer !~ /\n$/ )
您的最终版本:
if( substr( $buffer, -1, 1 ) ne '\n' )
除了引用换行符之外,这也是一个不错的选择,这样可以得到一个由反斜杠和小写n组成的两个字符串。也许你来自C语言,其中单字符是单引号,字符串是双引号?你想要
if( substr( $buffer, -1, 1 ) ne "\n" )
这个版本
if( substr( $buffer, -1, 1 ) !~ /\n/ )
正在进行不应该的正则表达式匹配,因为它正在检查单字符字符串与单字符正则表达式。下一个阅读代码的人会认为这很奇怪,并想知道你为什么会这样做。另外,回到速度方面,将字符串与正则表达式进行匹配要比仅与单个字符进行相等比较慢。Perl有两种字符串存储格式 其中一种格式使用相同数量的字节(1)来存储字符串可能包含的每个字符。正因为如此,而且Perl跟踪一个字符串使用了多少字节,因此
substr($x,-1)
在这种格式的字符串上的性能并不取决于字符串的长度
上述格式的问题是,它只能存储非常有限的字符范围。它可用于存储Unicode码点“Eric”和“êric”,但不能用于存储“ΕλΔα”。必要时(甚至不必要时),Perl将自动将字符串的存储格式切换为其他格式
第二种格式可以将任何Unicode代码点存储为字符。实际上,它可以存储任何32位或64位的值(取决于perl
的构建设置)。缺点是使用可变字节数来存储每个字符。因此,即使Perl知道整个字符串使用的字节数,它也不知道第一个字符从哪里开始。*要查找最后一个字符,它必须扫描整个字符串
这就是说,由于存储格式的属性,实际上很容易在固定时间内找到字符串的最后一个字符
use Inline C => <<'__END_OF_C__';
# O(1) version of substr($x,-1)
SV* last_char(SV* sv) {
STRLEN len;
const char* s = SvPV(sv, len);
if (!len)
return newSVpvn("", 0);
{
const U32 utf8 = SvUTF8(sv);
const char* p = s+len-1;
if (utf8) {
while (p != s && (*p & 0xC0) != 0xC0)
--p;
}
return newSVpvn_utf8(p, s+len-p, utf8);
}
}
__END_OF_C__
use Inline C=>Perl有两种字符串存储格式
其中一种格式使用相同数量的字节(1)来存储字符串可能包含的每个字符。正因为如此,而且Perl跟踪一个字符串使用了多少字节,因此substr($x,-1)
在这种格式的字符串上的性能并不取决于字符串的长度
上述格式的问题是,它只能存储非常有限的字符范围。它可用于存储Unicode码点“Eric”和“êric”,但不能用于存储“ΕλΔα”。必要时(甚至不必要时),Perl将自动将字符串的存储格式切换为其他格式
第二种格式可以将任何Unicode代码点存储为字符。实际上,它可以存储任何32位或64位的值(取决于perl
的构建设置)。缺点是使用可变字节数来存储每个字符。因此,即使Perl知道