perl-通过查找数组从二进制模式字符串中删除字节

时间:2019-03-26 19:03:28

标签: perl

我正在使用binmode()读取文件中的文件,并想去除与静态列表中的任何值匹配的字节值

@strip = (91,   92,   98,   107,   5,   64,   21,   13,   11,   12)

我在脚本中做什么

binmode($fh);
read($fh,$data,20);
%strip = (91=>1, 92=>1,98=>1,107=>1,5=>1,64=>1,21=>,13=>1,11=>1,12=>1); 
$data=~s/(.)/$strip{ord($1)} ? "" :$1/ge

恐怕,以正则表达式方式执行操作可能不正确,并且会产生一些不良结果。

有人可以建议更清洁,高效的替代方法吗?

1 个答案:

答案 0 :(得分:3)

regex引擎非常乐意对字节字符串进行操作(尽管使用\d可能没有任何意义),因此您的方法非常好。但是白色非常有效,可以加快速度。

如果我们在字节上使用chr而不是在所有读取的字符上使用ord怎么办?

my @to_strip = ( 5, 11, 12, 13, 21, 64, 91, 92, 98, 107 );
my %to_strip = map { chr($_) => 1 } @to_strip;

$data =~ s/(.)/ $strip{$1} ? "" :$1 /ge;

如果我们走得更远,甚至更快地做出替换选择,该怎么办?

my @to_strip = ( 5, 11, 12, 13, 21, 64, 91, 92, 98, 107 );
my %to_strip = map { chr($_) => 1 } @to_strip;
my %map = map { $to_strip{$_} ? "" : $_ } map chr, 0x00..0xFF;

$data =~ s/(.)/$map{$1}/sg;

但是我们仍在进行许多不必要的替换。如果我们搜索要替换的特定字符怎么办?

my @to_strip = ( 5, 11, 12, 13, 21, 64, 91, 92, 98, 107 );
my $pat = "[" . quotemeta( pack( 'C*', @to_strip ) ) . "]+";
my $re = qr/$pat/;

$data =~ s/$re//g;

这个速度快得多有以下三个原因:

  • 如前所述,我们大大减少了匹配数,从而减少了需要评估和连接替换表达式的次数。
  • 正则表达式引擎可以比Perl代码更快地检查匹配字符。
  • 我们消除了(相对而言)非常慢的捕获需求。

请记住,@to_strip%to_strip%map$pat$re只需要计算一次,而不是每个read一次。当我谈到上述速度时,我没有包括计算这些速度所需的时间,因为我认为您将进行多次读取和替换。


也就是说,如果合理地对要删除的字节进行硬编码,tr///d将为您提供最佳性能。

$data =~ tr/\x05\x0B-\x0D\x15\x40\x5B\x5C\x62\x6B//d;

使用动态列表中的tr///无效,因为tr///不会插值。我们必须求助于构建一个sub,而调用sub相对较慢。

my @to_strip = ( 5, 11, 12, 13, 21, 64, 91, 92, 98, 107 );
my $class = quotemeta( pack( 'C*', @to_strip ) );
my $inline_stripper = eval("sub { $_[0] =~ tr/$class//d; }");

$inline_stripper->($data);

以下是一种有效(但肯定不那么有效)的非正则表达式方法。

my @to_strip = ( 5, 11, 12, 13, 21, 64, 91, 92, 98, 107 );
my @to_strip_lookup; $to_strip_lookup[$_] = 1 for @to_strip;

$data = pack 'C*', grep !$to_strip_lookup[$_], unpack 'C*', $data