#perl
#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:
Механизм регулярных выражений вполне готов работать со строками байтов (хотя использование 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/x05x0B-x0Dx15x40x5Bx5Cx62x6B//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
Комментарии:
1. спасибо за ответ.. не изменит ли использование chr() в строке binmode символы
2. Re » не изменит ли использование chr() в строке binmode символы в строке binmode «, да? Ни одно из решений не использует
chr
для строки.3. просто любопытно.. это что-то вроде операции установки минус B, так можно ли это выполнить с двумя массивами байтовых значений?.
4. Вы могли бы рассмотреть список байтов для удаления набора, но не данные, потому что значения наборов уникальны и неупорядочены. Но вы не ошибаетесь. Решение без регулярных выражений, которое я только что добавил, в основном точно такое, как я бы сделал разницу в наборе (если бы элементы наборов начинались с массивов):
my %B = map { $_ => 1 } @B; my @AminusB = grep !$B{$_}, @A;
5.
list2re
из Data::Munge можно красиво обернуть логику создания регулярного выражения (хотя я не знаю, будет ли результирующее регулярное выражение таким же эффективным, как символьный класс в этом случае).