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

perl-在偶数位置进行匹配并删除不可打印的字符,perl,Perl,我有一个来自数据库表转储的hex2string "41424320202020200A200B000C" 我想做的是在偶数位置进行匹配,并检测打印时可能破坏字符串的控制字符。。i、 删除ascii null\x00、\n、\r、\f和\x80到\xFF等 我试着删除ascii空值 perl -e ' $x="41424320202020200A200B000C"; $x=~s/00//g; print "$x\n" ' 但结果不正确,因为它从空格x20的尾部十六进制值中删除了0,并将换行符x

我有一个来自数据库表转储的hex2string

"41424320202020200A200B000C"
我想做的是在偶数位置进行匹配,并检测打印时可能破坏字符串的控制字符。。i、 删除ascii null\x00、\n、\r、\f和\x80到\xFF等

我试着删除ascii空值

perl -e ' $x="41424320202020200A200B000C"; $x=~s/00//g; print "$x\n" '
但结果不正确,因为它从空格x20的尾部十六进制值中删除了0,并将换行符x0A的前导0删除,即
20 0A
2A

414243202020202A2B0C
我想要的是

414243202020202020

您可以尝试使用以下方法将字符串拆分为2个字节的子字符串:

输出

41424320202020200A200B000C
41424320202020200A200B0C
检测打印时可能破坏字符串的控制字符。。i、 删除ascii null\x00、\n、\r、\f和\x80到\xFF等

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

字符集中的字符类
[:print://code>匹配所有可打印字符,包括空格(但不包括控制字符,如换行符和换行符),我还添加了tab。然后它还会检查以确保字节在ASCII范围内(因为在许多地区,更高的字符仍然可以打印)

使用
/a
/r
的解决方案需要Perl5.14+


以上内容以以下字符串开头:

 41424320202020200A200B000C
使用
pack
将其转换为以下内容:

 ABC␠␠␠␠␠␊␠␋␀␌
 414243202020202020
替换将删除除TAB之外的所有非ASCII和所有不可打印字符,留下以下内容:

 ABC␠␠␠␠␠␠
$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
使用
解包
将其转换为以下内容:

 ABC␠␠␠␠␠␊␠␋␀␌
 414243202020202020


此解决方案不仅比以前的解决方案短,而且速度更快,因为它分配的变量少得多,并且只启动正则表达式匹配一次。

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


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

 ABC␠␠␠␠␠␠
$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
  • ASCII可打印文件(2016至7E16)
  • 选项卡(0916)
这意味着您希望排除以下字符:

  • 0016至0816
  • 0A16至1F16
  • 7F16至FF16
如果我们把这些按前导数字分组,我们得到

  • 0016至0816,0A16至0F16
  • 1016至1F16
  • 7F16
  • 8016至FF16
因此,我们可以使用以下方法:

 ABC␠␠␠␠␠␠
$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

这里的
13
是什么?是字符串的长度除以2我去掉了固定的数字13。见我的最新答案伟大的答案。。我正在构建一个带有备用匹配项的正则表达式,您已经对其进行了简化。除非使用了
/l
,否则不会查询活动区域设置,因此最后一句没有意义。您能否确认它将保留制表符和空格?我不想删除它们。只需将其添加到要保留的字符列表中即可。(我已经调整了答案)请您解释一下/a修饰符,该解决方案在我的cygwin中有效,但不确定它在我的企业中是否有效。只要您使用Perl 5.14或更高版本(对于
/r
/a
),它就会有效
/a
导致
[:print://code>仅匹配ASCII字符(00..7F)。添加了不需要5.14的替代方案。它们至少可以在5.6之前使用(尽管在Perl5.10之前,您需要将
say
替换为
print
)。。匹配上一个匹配结束的位置(如果是第一个匹配,则在字符串开头)。您可以想象每个模式的前缀都是隐式的
\G(?.s:)*?\K
。通过使用
\G(?s:..)?\K
代替,我们在寻找匹配时前进两个位置,而不是1。那么这不是多余的吗?。。抱歉,理解起来有点混乱…不,这是阻止
00
2009
中匹配的原因,这正是您要求解决的问题。例如,使用
s/\G(?:)*?\K00//sg
而不是
s/00//g
可以解决您的问题,如果您只想删除NUL。在这种情况下,它将在第一次出现时失败,对吗?。。004142?.. 但它成功了!!