У меня есть hex2string из дампа таблицы базы данных, которая похожа
"41424320202020200A200B000C"
То, что я хочу сделать, это сопоставить в четных позициях и обнаружить контрольные символы, которые могут разбить строку при печати .. т.е. удалить ноль ascii \ x00, \ n, \ r, \ f и \ x80 в \ xFF и т. д. ,
Я пытался удалить ASCII NULL, как
perl -e ' $x="41424320202020200A200B000C"; $x=~s/00//g; print "$x\n" '
Но результат неверен, так как он удалил 0 из конечного шестнадцатеричного значения пробела \ x20 и привел 0 новой строки \ x0A i.e 20 0A
к 2A
414243202020202A2B0C
Что я хотел
414243202020202020
4 ответа
say unpack("H*", pack("H*", "41424320202020200A200B000C") =~ s/[^\t[:print:]]//arg);
Или
my $hex = "41424320202020200A200B000C";
my $bytes = pack("H*", $hex);
$bytes =~ s/[^\t[:print:]]//ag;
$hex = unpack("H*", $bytes);
say $hex;
Или
my $hex = "41424320202020200A200B000C";
my $bytes = pack("H*", $hex);
$bytes =~ s/[^\t\x20-\x7E]//g;
$hex = unpack("H*", $bytes);
say $hex;
Решения, использующие /a
и /r
, требуют Perl 5.14+.
Сказанное начинается со следующей строки:
41424320202020200A200B000C
Он преобразуется в следующее с помощью pack
:
ABC␠␠␠␠␠␊␠␋␀␌
Подстановка удаляет все не-ASCII и все непечатаемые символы, кроме TAB, оставляя нам следующее:
ABC␠␠␠␠␠␠
Он преобразуется в следующее с помощью unpack
:
414243202020202020
Это решение не только короче, чем предыдущие решения, но и быстрее, поскольку оно выделяет гораздо меньше переменных и запускает совпадение с регулярным выражением только один раз.
обнаружить контрольные символы, которые могут разбить строку при печати. Т.е. удалить ноль ascii \ x00, \ n, \ r, \ f и \ x80 в \ xFF и т. д.
Основываясь на ответе Хакона (который удаляет только нулевые байты, а не все остальные):
#!/usr/bin/perl
use warnings;
use strict;
use feature qw/say/;
my $x="41424320202020200A200B000C";
say $x;
say grep { chr(hex($_)) =~ /[[:print:]\t]/ && hex($_) < 128 } unpack("(A2)*", $x);
Дает тебе
41424320202020200A200B000C
414243202020202020
Класс символов [:print:]
внутри набора символов совпадает со всеми печатными символами, включая пробел (но не с управляющими символами, такими как перевод строки и перевод строки), и я также добавил их на вкладку. Затем он также проверяет, находится ли байт в диапазоне ASCII (поскольку во многих локалях по-прежнему можно печатать старшие символы).
Можно работать непосредственно с шестнадцатеричной формой символов, но это намного сложнее. Я рекомендую против использования этого подхода. Этот ответ служит иллюстрацией того, почему это решение не было предложено.
Вы хотите исключить все символы, кроме следующих:
- ASCII печатные формы (от 20 16 до 7E 16 )
- TAB (09 16 )
Это означает, что вы хотите исключить следующие символы:
- От 00 16 до 08 16
- От 0A 16 до 1F 16
- 7F 16 - FF 16
Если мы сгруппируем их по начальным цифрам, мы получим
- От 00 16 до 08 16 , от 0A 16 до 0F 16
- От 10 16 до 1F 16
- 7F < суб > 16 суб >
- От 80 16 до FF 16
Поэтому мы можем использовать следующее:
$hex =~ s/\G(?:..)*?\K(?:0[0-8A-Fa-f]|7F|[189A-Fa-f].)//sg; # 5.10+
$hex =~ s/\G((?:..)*?)(?:0[0-8A-Fa-f]|7F|[189A-Fa-f].)/$1/sg; # Slower
Вы можете попробовать разбить строку на 2-байтовые подстроки, используя unpack
:
my $x="41424320202020200A200B000C";
say $x;
say join '', grep { $_ !~ /00/} unpack "(A2)*", $x;
Вывод :
41424320202020200A200B000C
41424320202020200A200B0C
Похожие вопросы
Новые вопросы
perl
Perl - это процедурный высокоуровневый динамический язык программирования общего назначения, известный своей собственной поддержкой регулярных выражений и возможностей синтаксического анализа строк. Пожалуйста, используйте этот тег для вопросов о Perl в целом. Для вещей, связанных с новым (но связанным) языком Raku (ранее «Perl 6»), используйте тег raku. Для регулярных выражений в стиле Perl на других языках используйте тег regex или, если они основаны на библиотеке PCRE, тег pcre.