一个简单的 C 程序是否可以模仿默认的 'xxd' 命令,使其差异输出 return 0?
Could a simple C program mimic the default 'xxd' command such that its diff'd output would return 0?
我正在尝试编写一个 C 可执行文件,它将产生与默认 xxd
命令相同的输出。例如,假设我有一个名为 test.txt
的相当小的文本文件和一个名为 myxxd
的可执行文件
所以,我先做一个benchmark来比较:
$ touch correct-xxdoutput.txt test-output.txt
$ xxd test.txt > correct-xxdoutput.txt
然后使用我的可执行文件进行相同的操作,但输出文件不同:
$ ./myxxd test.txt > test-output.txt
$ diff correct-xxdoutput.txt test-output.txt
$
我已经非常接近于一些猜测了,但是我的格式总是不知何故错误,而且我真的不明白 xxd
是如何生成 hexDumps 的。感觉我只是在这里采取了完全错误的方法,但也许以我目前的 C 知识水平,这项任务超出了我的潜力。
我的代码(另请参阅:https://pastebin.com/Vjkm8Wb4):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SIZE 256
//Prototypes
void hexDump(void*, int);
int main(int argc, char *argv[])
{
//Create and open filestream
FILE *myfile;
myfile =fopen(argv[1],"rb");
for ( ; ; )
{
unsigned char buffer[SIZE];
size_t n = fread(buffer, 1, SIZE, myfile);
if (n > 0)
hexDump(buffer, n);
if (n < SIZE)
break;
}
fclose(myfile);
return 0;
}
void hexDump (void *addr, int len)
{
int i;
unsigned char bufferLine[17];
unsigned char *pc = (unsigned char*)addr;
for (i = 0; i < len; i++)
{
if ((i % 16) == 0)
{
if (i != 0)
printf (" %s\n", bufferLine);
if (pc[i] == 0x00) exit(0);
printf ("%08x: ", i);
}
// Prints Hexcdoes that represent each chars.
printf ("%02x", pc[i]);
if ((i % 2) == 1)
printf (" ");
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
{
bufferLine[i % 16] = '.';
}
else
{
bufferLine[i % 16] = pc[i];
}
bufferLine[(i % 16) + 1] = '[=14=]'; //Clears the next array buffLine
}
while ((i % 16) != 0)
{
printf (" ");
i++;
}
printf (" %s\n", bufferLine);
}
您的代码存在多个问题,包括:
- 您没有检查是否有要打开的文件名。
- 您没有检查是否打开了指定的文件。
- 您没有处理输出偏移量的机制,因此第一个块之后行开头的地址是错误的。
- 您的代码测试零字节并在遇到零字节时静默退出。这很糟糕——两次。一次是因为旨在处理二进制数据的程序必须处理零字节以及 1..255 中的值;一次是因为静默退出(并声称
exit(0)
成功启动)是不好的。您应该报告问题(关于标准错误,而不是标准输出)并以错误状态退出 - 非零状态。
核心格式似乎基本没问题;在文件末尾填充短数据行也有问题。
我想出了这个代码,它与你的代码非常相似(但重新格式化以至少适应我的一些风格偏见——但大多数时候我的风格与你的相差不远):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 256
void hexDump(size_t, void *, int);
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s file\n", argv[0]);
exit(EXIT_FAILURE);
}
FILE *myfile = fopen(argv[1], "rb");
if (myfile == 0)
{
fprintf(stderr, "%s: failed to open file '%s' for reading\n", argv[0], argv[1]);
exit(EXIT_FAILURE);
}
unsigned char buffer[SIZE];
size_t n;
size_t offset = 0;
while ((n = fread(buffer, 1, SIZE, myfile)) > 0)
{
hexDump(offset, buffer, n);
if (n < SIZE)
break;
offset += n;
}
fclose(myfile);
return 0;
}
void hexDump(size_t offset, void *addr, int len)
{
int i;
unsigned char bufferLine[17];
unsigned char *pc = (unsigned char *)addr;
for (i = 0; i < len; i++)
{
if ((i % 16) == 0)
{
if (i != 0)
printf(" %s\n", bufferLine);
// Bogus test for zero bytes!
//if (pc[i] == 0x00)
// exit(0);
printf("%08zx: ", offset);
offset += (i % 16 == 0) ? 16 : i % 16;
}
printf("%02x", pc[i]);
if ((i % 2) == 1)
printf(" ");
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
{
bufferLine[i % 16] = '.';
}
else
{
bufferLine[i % 16] = pc[i];
}
bufferLine[(i % 16) + 1] = '[=10=]';
}
while ((i % 16) != 0)
{
printf(" ");
if (i % 2 == 1)
putchar(' ');
i++;
}
printf(" %s\n", bufferLine);
}
当 运行 在您的原始源代码上与系统 xxd
的输出进行比较时,没有差异。我还对照一个只有 16 个字符(abcdefghijklmno
加一个换行符)的文件检查了它;那里的输出也是一样的。我检查了它自己的二进制文件——发现并修复了零字节和未经通知的提前退出问题。
我正在尝试编写一个 C 可执行文件,它将产生与默认 xxd
命令相同的输出。例如,假设我有一个名为 test.txt
的相当小的文本文件和一个名为 myxxd
所以,我先做一个benchmark来比较:
$ touch correct-xxdoutput.txt test-output.txt
$ xxd test.txt > correct-xxdoutput.txt
然后使用我的可执行文件进行相同的操作,但输出文件不同:
$ ./myxxd test.txt > test-output.txt
$ diff correct-xxdoutput.txt test-output.txt
$
我已经非常接近于一些猜测了,但是我的格式总是不知何故错误,而且我真的不明白 xxd
是如何生成 hexDumps 的。感觉我只是在这里采取了完全错误的方法,但也许以我目前的 C 知识水平,这项任务超出了我的潜力。
我的代码(另请参阅:https://pastebin.com/Vjkm8Wb4):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define SIZE 256
//Prototypes
void hexDump(void*, int);
int main(int argc, char *argv[])
{
//Create and open filestream
FILE *myfile;
myfile =fopen(argv[1],"rb");
for ( ; ; )
{
unsigned char buffer[SIZE];
size_t n = fread(buffer, 1, SIZE, myfile);
if (n > 0)
hexDump(buffer, n);
if (n < SIZE)
break;
}
fclose(myfile);
return 0;
}
void hexDump (void *addr, int len)
{
int i;
unsigned char bufferLine[17];
unsigned char *pc = (unsigned char*)addr;
for (i = 0; i < len; i++)
{
if ((i % 16) == 0)
{
if (i != 0)
printf (" %s\n", bufferLine);
if (pc[i] == 0x00) exit(0);
printf ("%08x: ", i);
}
// Prints Hexcdoes that represent each chars.
printf ("%02x", pc[i]);
if ((i % 2) == 1)
printf (" ");
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
{
bufferLine[i % 16] = '.';
}
else
{
bufferLine[i % 16] = pc[i];
}
bufferLine[(i % 16) + 1] = '[=14=]'; //Clears the next array buffLine
}
while ((i % 16) != 0)
{
printf (" ");
i++;
}
printf (" %s\n", bufferLine);
}
您的代码存在多个问题,包括:
- 您没有检查是否有要打开的文件名。
- 您没有检查是否打开了指定的文件。
- 您没有处理输出偏移量的机制,因此第一个块之后行开头的地址是错误的。
- 您的代码测试零字节并在遇到零字节时静默退出。这很糟糕——两次。一次是因为旨在处理二进制数据的程序必须处理零字节以及 1..255 中的值;一次是因为静默退出(并声称
exit(0)
成功启动)是不好的。您应该报告问题(关于标准错误,而不是标准输出)并以错误状态退出 - 非零状态。
核心格式似乎基本没问题;在文件末尾填充短数据行也有问题。
我想出了这个代码,它与你的代码非常相似(但重新格式化以至少适应我的一些风格偏见——但大多数时候我的风格与你的相差不远):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 256
void hexDump(size_t, void *, int);
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s file\n", argv[0]);
exit(EXIT_FAILURE);
}
FILE *myfile = fopen(argv[1], "rb");
if (myfile == 0)
{
fprintf(stderr, "%s: failed to open file '%s' for reading\n", argv[0], argv[1]);
exit(EXIT_FAILURE);
}
unsigned char buffer[SIZE];
size_t n;
size_t offset = 0;
while ((n = fread(buffer, 1, SIZE, myfile)) > 0)
{
hexDump(offset, buffer, n);
if (n < SIZE)
break;
offset += n;
}
fclose(myfile);
return 0;
}
void hexDump(size_t offset, void *addr, int len)
{
int i;
unsigned char bufferLine[17];
unsigned char *pc = (unsigned char *)addr;
for (i = 0; i < len; i++)
{
if ((i % 16) == 0)
{
if (i != 0)
printf(" %s\n", bufferLine);
// Bogus test for zero bytes!
//if (pc[i] == 0x00)
// exit(0);
printf("%08zx: ", offset);
offset += (i % 16 == 0) ? 16 : i % 16;
}
printf("%02x", pc[i]);
if ((i % 2) == 1)
printf(" ");
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
{
bufferLine[i % 16] = '.';
}
else
{
bufferLine[i % 16] = pc[i];
}
bufferLine[(i % 16) + 1] = '[=10=]';
}
while ((i % 16) != 0)
{
printf(" ");
if (i % 2 == 1)
putchar(' ');
i++;
}
printf(" %s\n", bufferLine);
}
当 运行 在您的原始源代码上与系统 xxd
的输出进行比较时,没有差异。我还对照一个只有 16 个字符(abcdefghijklmno
加一个换行符)的文件检查了它;那里的输出也是一样的。我检查了它自己的二进制文件——发现并修复了零字节和未经通知的提前退出问题。