用十六进制数值替换不可打印字符

用十六进制数值替换不可打印字符

我有一个严重损坏的 Sqlite 文件 将所有内容转储到 sql 文件并将其加载到新文件中的常用技巧不起作用,但使用十六进制编辑器我可以看到我需要恢复的数据就在那里

我遇到了这个模式

vim 可以只显示 ASCII 字符,而将其他字节视为二进制数据吗?

让 vi 将不可打印的字符显示为十六进制

这太棒了,会告诉我

14>>07>>泰斯蒂·麦克泰森先生[电子邮件受保护]

但是有没有办法在显示时将其写入文件?

因此,将 vi 在其缓冲区中显示为 <14> 的十六进制值,实际上将其更改为文本文件中的那些字符

我可以在 vi 中执行正则表达式搜索替换来执行此操作,但随后我必须一次对每个不可打印字符执行此操作,而且这是一个相当大的文件

稍后,我计划将<14><07>处理成它应该表示的16位整数,但首先我需要能够将它们作为真实字符放入文本文件中

提前谢谢了

答案1

您可能会查看xxd其中附带的内容vim并转储十六进制数据和列中的可打印字符。如果您编辑十六进制,您可以将数据推回xxd -r以将其转换回二进制。

然而,看看你的最终目标,你可能需要一些更强大的东西perl,比如,我不是这方面的专家,但你可能会发现以下有用:

#!/usr/bin/perl
# https://unix.stackexchange.com/a/452784/119298
use strict;
sub fn{ 
    my ($ch,$ch2,$rest) = @_;
    return sprintf("%5u",(ord($ch)<<8)|ord($ch2)).$rest;
}
my $data = join("",<>);
$data =~ s/(.)(.)([a-zA-Z][ -~]{10,})/fn($1,$2,$3)/ge;
print $data;

它将所有数据从 stdin 读入变量$data,然后s/.../.../g对由任意 2 个字节后跟一个字母字符(范围 az 和 AZ),后跟 10 个或更多可打印字符(在范围空间到波形符,并假设 C 语言环境)。这些部分通过使用()分成 3 个独立的部分来捕获,并通过函数的调用来替换fn。这就是e最后的意思。

该函数仅返回转换为整数的 2 个字节的字符串打印,并与第三个未更改的参数连接。

为了提供帮助,这里有一个更简单的版本,它只执行您想要的操作,将非打印字符替换为<..>

my $data = join("",<>);
$data =~ s/([^ -~\n])/sprintf("<%02x>",ord($1))/ge;
print $data;

这里的模式比较简单,即不可打印字符(和换行符)的范围,具有^含义不是。当查看一个简单的 sqlite 文件时,我发现紧邻文本数据之前的字符通常是可打印字符。这就是为什么我使用了一种测试字母起始字符的模式,但您可能需要使用更好的启发式方法。

答案2

下面将替换 0x00 - 0x20 范围内的非打印字符(不包括 CR、LF、制表符和空格),#xDD#其中DD是字符的十六进制表示形式(感谢这个问题用于很好地定义范围):

sed $( (seq 0 8; seq 11 12; seq 14 31) | awk '{ printf("s/\\x%02x/#x%02x#/;", $0, $0) }' )

我用来seq生成用于替换的字符范围,并awk生成模式sed- 后者进行实际的替换。

你可以用这个来测试它:

seq 0 32 | awk '{$0 =$0 sprintf("%c",$0)} 1' | sed $( (seq 0 8; seq 11 12; seq 14 31) |  awk '{ printf("s/\\x%02x/#x%02x#/;", $0, $0) }' )

FWIW 对于在搜索词中找到此内容的人来说,在 alpine docker 映像(我想要处理自己的损坏文件的地方)sed不喜欢 NULL 字符,因此我必须像这样解决它:

sed $( (seq 1 8; seq 11 12; seq 14 31) |  awk '{ printf("s/\\x%02x/#x%02x#/;", $0, $0) }' ) | tr '\0' '\1' | sed 's/\x01/#x00#/'

相关内容