我有一个严重损坏的 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#/'