简体   繁体   中英

Hexdump text and binary files in C

I am writing a code in C performing hexdump in both text and binary files. My output in text files are correct but when I tried performing hexdump in a binary file, I got garbages. I would like to ask for your help if which part of my code is wrong and how should I correct my mistakes. Thanks.

#include <stdio.h>
#define OFFSET 16

main(int argc, char const *argv[]) 
{
    FILE *fp;
    char buff[OFFSET];
    int read;
    int address = 0;
    int i;

    if (argc != 2){
        exit(0);
    }

    fp = fopen(argv[1], "rb");

    while ((read = fread(buff, 1, sizeof buff, fp)) > 0){
        printf("%08x ", address);
        address += OFFSET;

        //print hex values 
        for (i = 0; i < OFFSET; i++){

            if(i >= read){
                buff[i] = 0;
            }
            if(buff[i] >= 33 && buff[i] <= 255 || buff[i] != 00){
                printf("%02x ", buff[i]);
            }
            if(buff[i] == 00){
                printf("   ");
            }
        }

        //print ascii values
        for (i = 0; i < OFFSET; i++) {
            printf("%c", (buff[i] >= 33 && buff[i] <= 255 ? buff[i] : ' '));
        }
        printf("\n");
    }

    fclose(fp);
}

You have a couple of errors in logic. First as specified in the comment, all characters are equally important within a binary file. There is no need (and you shouldn't) test if(buff[i] >= 33 && buff[i] <= 255 || buff[i] != 00) for your binary output.

The proper declarations for main are int main (void) and int main (int argc, char **argv) (which you will see written with the equivalent char *argv[] ). See: C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570) . See also: See What should main() return in C and C++?

Next with your binary output, you are attempting to print an unsigned value with %02x , but your are passing a signed character. If the char value is negative, you are trying to output the sign-extended value with outputs the full width of the unsigned value ( 02x will pad the field to 2 characters, but does not prevent more than two characters from printing). You have a couple of options, first use the hh length modifier to limit the type to 1-byte, and second simply cast the value to (unsigned char) , eg

            printf("%02hhx ", (unsigned char)buff[i]);

You logic is also a bit cumbersome. You should use if ... else if ... else to handle your binary cases. Further, you are outputting two spaces when either i >= read || buff[i] == 0 i >= read || buff[i] == 0 , so you may as well combine the test.

A short rewrite could look something like the following (which will read from the file given as the 1st argument -- or from stdin if no argument is given)

#include <stdio.h>

#define OFFSET 16

int main (int argc, char const *argv[]) 
{
    char buff[OFFSET] = "";
    int read, address = 0, i;
    FILE *fp = argc > 1 ? fopen (argv[1], "rb") : stdin;

    if (!fp) {
        perror ("fopen");
        return 1;
    }

    while ((read = fread(buff, 1, sizeof buff, fp)) > 0) {
        printf("%08x ", address);
        address += OFFSET;

        for (i = 0; i < OFFSET; i++)    /* print hex values */
            if (i >= read || buff[i] == 0)
                printf("   ");
            else
                printf("%02hhx ", (unsigned char)buff[i]);

        fputs ("| ", stdout); /* optional separator before ASCII */

        for (i = 0; i < OFFSET; i++)    /* print ascii values */
            printf("%c", (buff[i] >= ' ' && buff[i] <= '~' ? buff[i] : ' '));
        putchar ('\n'); /* use putchar to output single character */
    }

    if (fp != stdin)
        fclose (fp);
}

(note: if your compiler does not support the hh prefix, the cast itself will suffice)

Look things over and let me know if you have further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM