0

我正在尝试创建像 xxd 这样的 hexdump,但我正在尝试解决一些差异。目前,该程序每行处理 10 个字符,如最右列所示,而 xxd 中为 16 个。它也只显示每列 1 个八位字节,而不是成对的 2 个八位字节。

xxd

 0000000: 2369 6e63 6c75 6465 203c 7374 6469 6f2e  #include <stdio.

我的输出

 0:  23 69 6E 63 6C 75 64 65 20 3C  #include <     

编辑:

为了添加一些说明,我试图实现两件事。1) 我希望这个程序能xxd 一样输出。为此,它需要输出 32 个十六进制数字(8x 列,每列 4)。2)我还希望程序在 xxd 中列出行的 4 列中的十六进制数字。

我试图将下面源代码中的“10”编辑为“12”,但它会在输出中产生错误,这似乎是一个神奇的数字

资源:

 #include <stdio.h>    
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <ctype.h>

 #define BYTE_OFFSET_INIT 8
 #define CHAR_OFFSET_INT  39
 #define LINE_LENGTH 50

 static void print_e(int e, char *program, char *file)
 {
   fprintf(stderr, "%s: %s: %s\n", program, file, strerror(e));
 }
 static void print_line(char *line)
 {
   int i;  
 /* sprintf leaves terminators mid-line, so clear them out so we can print the full line */
   for (i = BYTE_OFFSET_INIT; i < CHAR_OFFSET_INT; i++)
     if (line[i] == '\0')
       line[i] = ' ';
   printf("%s\n", line);
 }

 int main(int argc, char *argv[])
 {
   char line[LINE_LENGTH + 1];

   int ch;
   int character = 0;
   int line_offset = 0;
   int byte_offset = BYTE_OFFSET_INIT, char_offset = CHAR_OFFSET_INT;

   if (argc != 2) {
     fprintf(stderr, "Usage: %s [file]\n", argv[0]);
     exit(EXIT_FAILURE);
   }
   FILE *fp = fopen(argv[1], "rb");
   if (!fp) {
     print_e(errno, argv[0], argv[1]);
     exit(EXIT_FAILURE);
   }

   printf("Offset              Bytes              Characters\n");
   printf("------  -----------------------------  ----------\n");

   while ((ch = fgetc(fp)) != EOF) {
     if (character == 0) {
       sprintf(line, "%6d  ", line_offset);
       line_offset += 10;
     }
     sprintf(line + byte_offset, "%02X ", ch);
     sprintf(line + char_offset, "%c", isprint(ch) ? ch : '.');
     character++;
     char_offset++;
     byte_offset += 3;

     if (character == 10) {
       print_line(line);
       character = 0;
       char_offset = CHAR_OFFSET_INT;
       byte_offset = BYTE_OFFSET_INIT;
     }
   }
   if (ferror(fp)) {
     print_e(errno, argv[0], argv[1]);
     exit(EXIT_FAILURE);
   }

   if (character > 0)
     print_line(line);

   if (fclose(fp) == EOF) {
     print_e(errno, argv[0], argv[1]);
     exit(EXIT_FAILURE);
   }
   return 0;
 }
4

1 回答 1

0

虽然可以一次扫描一个字节并将其写入输出字符串的正确位置,但这绝不是必要的。DISPLAY_LENGTH一次读取字节并循环两次读取的字节数要容易得多;首先输出十六进制表示,然后再次输出 ASCII 字符。唯一的(次要)警告是在文件末尾做什么;但是由于fread返回字符数,您可以继续计算并输出空格,只要需要填充十六进制行即可。

这导致以下程序。DISPLAY_LENGTH是每行显示的字节总数,GROUP_BYTES是每个十六进制组中的单个字节数(将其设置为1将显示“常规”间隔的十六进制输出,2将按照您的xxd示例进行分组,并且更高的值也应该起作用)。

我找到了一些有趣的公式来正确地使文本居中Bytes并计算要为分隔符显示多少破折号。其余的非常简单。

xxd除了您的单行示例之外,我不知道输出是什么样的,所以我使用stat提前读出文件的长度(还有一个额外的机会显示“不是文件”的错误 - 尝试以文件夹为例)并显示正确数量的破折号和空格以排列行计数器。我将此值设置为至少 6,因此文本始终有空间Offset

如果您的编译器不是现代编译器,它可能会抱怨%zu格式字符串。如果是这样,请使用%lu; 您可能还需要更改所有出现的size_tto unsigned long

#include <stdio.h>    
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>

#define DISPLAY_LENGTH  21
#define GROUP_BYTES     2

static void print_e(int e, char *program, char *file)
{
    fprintf(stderr, "%s: %s: %s\n", program, file, strerror(e));
}

int main(int argc, char *argv[])
{
    size_t i;
    struct stat fs;
    int n_digit;
    unsigned char read_buf[DISPLAY_LENGTH];
    size_t bytes_read, cpos = 0;

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s [file]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    FILE *fp = fopen(argv[1], "rb");
    if (!fp)
    {
        print_e (errno, argv[0], argv[1]);
        exit(EXIT_FAILURE);
    }

    if (stat(argv[1], &fs) == -1)
    {
        print_e (errno, argv[0], argv[1]);
        exit(EXIT_FAILURE);
    }

    if ((fs.st_mode & S_IFMT) != S_IFREG)   /* regular file */
    {
        fprintf(stderr, "Not a regular file: %s\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    n_digit = 0;
    while (fs.st_size > 0)
    {
        fs.st_size /= 10;
        n_digit++;
    }
    if (n_digit < 6)
        n_digit = 6;

    printf("%*s  ", n_digit, "Offset");
    printf("%*s%-*s", ((2*DISPLAY_LENGTH+(DISPLAY_LENGTH+GROUP_BYTES-1)/GROUP_BYTES)+2)/2, "Bytes", ((2*DISPLAY_LENGTH+(DISPLAY_LENGTH+GROUP_BYTES-1)/GROUP_BYTES)+2-5)/2, "");
    printf ("  Characters\n");
    for (i=0; i<n_digit; i++)
        printf ("-");

    printf("  ");

    for (i=1; i<2*DISPLAY_LENGTH+(DISPLAY_LENGTH+GROUP_BYTES-1)/GROUP_BYTES; i++)
        printf ("-");
    printf ("  ");

    for (i=0; i<DISPLAY_LENGTH; i++)
        printf ("-");
    printf ("\n");

    while ( (bytes_read = fread (read_buf, 1, DISPLAY_LENGTH, fp)))
    {
        printf ("%*zu ", n_digit, cpos);

        for (i=0; i<bytes_read; i++)
        {
            if (!(i % GROUP_BYTES))
                printf (" ");
            printf ("%02X", read_buf[i]);
        }
        while (i < DISPLAY_LENGTH)
        {
            if (!(i % GROUP_BYTES))
                printf (" ");
            printf ("  ");
            i++;
        }

        printf ("  ");

        for (i=0; i<bytes_read; i++)
            printf ("%c", isprint(read_buf[i]) ? read_buf[i] : '.');

        printf ("\n");

        cpos += bytes_read;
    }
    if (ferror(fp))
    {
        print_e (errno, argv[0], argv[1]);
        exit(EXIT_FAILURE);
    }

    if (fclose(fp))
    {
        print_e (errno, argv[0], argv[1]);
        exit(EXIT_FAILURE);
    }
    return 0;
}

示例输出,显示其自己编译的可执行文件,显示长度为 21,每 2 个字节分组:

Offset                        Bytes                           Characters
------  ----------------------------------------------------  ---------------------
     0  CFFA EDFE 0700 0001 0300 0080 0200 0000 0D00 0000 70  ....................p
    21  0600 0085 0020 0000 0000 0019 0000 0048 0000 005F 5F  ..... .........H...__
    42  5041 4745 5A45 524F 0000 0000 0000 0000 0000 0000 00  PAGEZERO.............
... (673 very similar lines omitted) ...
 14196  7075 7473 005F 7374 6174 2449 4E4F 4445 3634 005F 73  puts._stat$INODE64._s
 14217  7472 6572 726F 7200 6479 6C64 5F73 7475 625F 6269 6E  trerror.dyld_stub_bin
 14238  6465 7200 0000                                        der...
于 2014-10-11T23:57:41.370 回答