perl - удаляет байты из строки binmode, просматривая массив - PullRequest
1 голос
/ 26 марта 2019

Я читаю файл кусками, используя 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 Ответ

3 голосов
/ 26 марта 2019

Движок регулярных выражений идеально подходит для работы со строками байтов (хотя использование \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/// не интерполирует.Мы должны прибегнуть к созданию подпрограммы, и вызов подпрограммы является относительно медленным.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...