我想以编程方式读取二进制文件中的文本/字符串。
我的目标的确切替代方法是Linux中的strings
shell命令。
当我运行strings -n 4 /bin/dd
shell命令时,它会打印818行文本。
如何像strings
命令一样找到二进制形式的所有字符串?
我的代码使用read
而不是fgetc
,并在找到EOF之后为其余文本添加了打印块。
它在/bin/dd
中可以找到813个单词,但是strings
仍然可以找到818个单词。有什么区别?
另一个问题;您可以为此代码提出性能改进建议吗?我猜read(1)
并不是最快的方法。
最新更新的代码
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
bool isPrintable(unsigned char c)
{
if(c >= 0x20 && c <= 0x7e || c == 0x09)
{
return true;
}
return false;
}
int main(int argc, char * argv [])
{
char buffer[300];
char *p = buffer;
char ch;
int fd;
if(argc < 2)
{
printf("Usage: %s file", argv[0]);
return 1;
}
fd = open(argv[1], O_RDONLY);
if(0 <= fd)
{
while(1 == read(fd, &ch, 1))
{
if(isPrintable(ch) && (p - buffer < sizeof(buffer) - 3))
{
*p++ = ch;
}
else
{
if(p - buffer >= 4) // print collected text
{
*p++ = '\n';
*p++ = '\0';
printf("%s", buffer);
}
p = buffer;
}
}
if(p - buffer >= 4) // print the rest, if any
{
*p++ = '\n';
*p++ = '\0';
printf("%s", buffer);
}
close(fd);
}
else
{
printf("Could not open %s\n", argv[1]);
return 1;
}
return 0;
}
这是mystrings
和strings
的性能指标。 strings
可以在更短的时间内找到更多文本。
$ time ./mystrings /lib/i386-linux-gnu/libc-2.27.so | wc -l
11852
real 0m0,917s
user 0m0,271s
sys 0m0,629s
$ time strings /lib/i386-linux-gnu/libc-2.27.so | wc -l
12026
real 0m0,028s
user 0m0,027s
sys 0m0,000s
即使我使用fopen
,fread
,fclose
也不是那么快:
$ time ./mystrings2 /lib/i386-linux-gnu/libc-2.27.so | wc -l
11852
real 0m0,084s
user 0m0,070s
sys 0m0,004s
我也欢迎任何有关性能改进的建议。
答案 0 :(得分:3)
您必须包含制表符。它们的十六进制代码为0x09。
您可以通过将其添加到可打印的测试中来对其进行修复:
if(c >= 0x20 && c <= 0x7e || c == 0x09)
哦,我不知道为什么该程序在此人的/bin/dd
中找到813个单词,而strings
找到818个单词。为什么有人会认为我会这么做?
但是,我确实有一个编译器和一个Unix系统,所以我可以做一些研究以找出答案。
首先,我在系统上进行了尝试:
$ ./yourprogram /bin/dd > yours && wc -l yours
807 yours
$ strings -n 4 /bin/dd > theirs && wc -l theirs
812 theirs
好吧,数字不同但还是有所不同。然后,我查看了差异:
$ diff -u yours theirs
--- yours 2018-07-17 15:13:27.188357492 -0700
+++ theirs 2018-07-17 15:13:56.905429280 -0700
@@ -182,7 +182,7 @@
ATUH
t9[]A\
[]A\
-[]A\
+8 []A\
AUAT1
[]A\A]
HiD$
@@ -210,7 +210,9 @@
XZL;t$
\$ I
AUATI
+;'u H
[]A\A]
+ v*H
这很麻烦,但是它表明您发现[]A\
而strings
发现8 []A\
。检查文件将其显示为Tab字符。然后,我可以创建一个测试用例:
$ printf 'hello\tworld' > file
$ strings file
hello world
$ ./yourprogram file
hello
world
因此,程序似乎无法识别Tab,而strings
可以识别。程序为什么不认为它可打印?
我在man ascii
中进行了查找:
Oct Dec Hex Char
───────────────────────────────────────
011 9 09 HT '\t' (horizontal tab)
我将其与代码查找的内容进行了比较。我可以在调试器中运行它,也可以添加printf
语句来尝试确定为什么它不能识别0x09,但是我可以看到它要求字符至少为0x20才能使其可打印。
我更新了isPrintable
,将其添加为特殊情况:
if(c >= 0x20 && c <= 0x7e || c == 0x09)
然后重新编译并重新运行:
$ ./yourprogram /bin/dd | wc -l
812
现在计数匹配,我可以将其发布为答案,并假装就像我使用了一些哈利波特修补魅力或秘密关卡锁定功能,而不仅仅是研究和测试。