perl-在偶数位置匹配并删除不可打印的字符

时间:2019-05-01 11:27:31

标签: perl

我从数据库表转储中得到了一个十六进制字符串,类似于

"41424320202020200A200B000C"

我要做的是在偶数位置匹配并检测在打印时可能破坏字符串的控制字符。即,将ascii null \ x00,\ n,\ r,\ f和\ x80删除为\ xFF等等。

我尝试删除

之类的ascii null
perl -e ' $x="41424320202020200A200B000C"; $x=~s/00//g; print "$x\n" '

但是结果不正确,因为它从空格\ x20的尾部十六进制值中删除了0,并将换行符\ x0A的前导0删除,即20 0A2A

414243202020202A2B0C

我想要的是

414243202020202020

4 个答案:

答案 0 :(得分:2)

  

检测在打印时可能会破坏字符串的控制字符。即,将ascii null \ x00,\ n,\ r,\ f和\ x80删除为\ xFF等。

基于Hakon的答案(仅去除nul字节,而不去除所有其他字节):

#!/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范围内(因为在许多语言环境中仍然可以打印更高的字符)。

答案 1 :(得分:2)

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 :(得分:2)

可以直接使用字符的十六进制形式,但是要复杂得多。我建议不要使用这种方法。这个答案可以说明为什么没有提出该解决方案。


您希望排除以下所有字符:

  • 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

答案 3 :(得分:1)

您可以尝试使用unpack将字符串分成2个字节的子字符串:

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

输出

41424320202020200A200B000C
41424320202020200A200B0C