У меня есть 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
3
stack0114106 1 Май 2019 в 14:27

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

Это решение не только короче, чем предыдущие решения, но и быстрее, поскольку оно выделяет гораздо меньше переменных и запускает совпадение с регулярным выражением только один раз.

2
ikegami 1 Май 2019 в 17:20

обнаружить контрольные символы, которые могут разбить строку при печати. Т.е. удалить ноль 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 (поскольку во многих локалях по-прежнему можно печатать старшие символы).

2
Shawn 1 Май 2019 в 13:20

Можно работать непосредственно с шестнадцатеричной формой символов, но это намного сложнее. Я рекомендую против использования этого подхода. Этот ответ служит иллюстрацией того, почему это решение не было предложено.


Вы хотите исключить все символы, кроме следующих:

  • 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
ikegami 1 Май 2019 в 18:32

Вы можете попробовать разбить строку на 2-байтовые подстроки, используя unpack:

my $x="41424320202020200A200B000C";
say $x;
say join '', grep { $_ !~ /00/} unpack "(A2)*", $x;

Вывод :

41424320202020200A200B000C
41424320202020200A200B0C
1
Håkon Hægland 1 Май 2019 в 12:18