Hunter McMillen
Hunter McMillen

Reputation: 61515

C read X bytes from a file, padding if needed

I am trying to read in an input file 64 bits at a time, then do some calculations on those 64 bits, the problem is I need to convert the ascii text to hexadecimal characters. I have searched around but none of the answers posted seem to work for my situation.

Here is what I have:

int main(int argc, int * argv)
{
   char buffer[9];
   FILE *f;
   unsigned long long test;

   if(f = fopen("input2.txt", "r"))
   {
     while( fread(buffer, 8, 1, f) != 0) //while not EOF read 8 bytes at a time
     {
       buffer[8] = '\0';
       test = strtoull(buffer, NULL, 16); //interpret as hex
       printf("%llu\n", test);
       printf("%s\n", buffer);
     }
     fclose(f);
   }
}

For an input like this:

"testing string to hex conversion"

I get results like this:

0 
testing  
0 
string t 
0 
o hex co 
0 nversion

Where I would expect:

74 65 73 74 69 6e 67 20 <- "testing" in hex
testing

73 74 72 69 6e 67 20 74 <- "string t" in hex
string t

6f 20 68 65 78 20 63 6f <- "o hex co" in hex
o hex co

6e 76 65 72 73 69 6f 6e <- "nversion" in hex
nversion

Can anyone see where I misstepped?

Upvotes: 0

Views: 3096

Answers (5)

Morpfh
Morpfh

Reputation: 4093

Consider using limits.h as well.

I went a bit overboard but perhaps some of this fits:

Edit: [
Ehrmf. Perhaps a define of BITS_ULL is more fitting to your quest.
I.e. something in the direction of:

#define BITS_ULL  (sizeof(unsigned long long) * CHAR_BIT)
#define BYTE_ULL  (sizeof(unsigned long long))

And then read BYTE_ULL bytes, but make shure to check size of read bytes and not if it is -1 as latter could be a smash. I'm a bit unsure what you mean by "calculations" on read bits.

You could read BYTE_ULL bytes and cast to unsigned long long by address of buffer[0], or bit shift taking byte order into consideration. Or former and sort bytes with char pointer.

Also note that I have used len instead of null terminated / C string.

Oh, this is lots of fun :) - I'm learning, and this kind of hacking is heaven.
]

#include <stdio.h>
#include <limits.h> /* BITS */
#include <ctype.h>  /* isprint() */

#define CHUNK_BITS  62
#define CHUNK_CHAR  (CHUNK_BITS / CHAR_BIT)
#define HEX_WIDTH   2

/* print len hex values of s, separate every sep byte with space, 
 * but do not add trailing space. */
void prnt_cshex(const char *s, int len, int sep)
{
    const unsigned char *p = (const unsigned char*)s;
    int i;

    for (i = 1; i <= len; ++p, ++i)
        fprintf(stdout,
                "%02x"
                "%s",
                *p, 
                (i < len && !((i)%sep) ? " " : ""));
}

/* Print len bytes of s, print dot if !isprint() */    
void prnt_csbytes(const char *s, int len)
{
    int i = 0;

    for (i = 0; i < len; ++s, ++i)
        fprintf(stdout,
                "%c",
                (isprint(*s) ? *s : '.'));
}

/* Pass file as first argument, if none, use default "input.txt" */
int main(int argc, char *argv[])
{
    const char *fn = "input.txt";
    FILE *fh;
    char buffer[CHUNK_CHAR];
    const char *p = &buffer[0];
    size_t k;

    if (argc > 1)
        fn = argv[1];

    if ((fh = fopen(fn, "rb")) == NULL) {
        fprintf(stderr, " * Unable to open \"%s\"\n", fn);
        goto fail_1;
    }

    fprintf(stdout,
        "Processing \"%s\"\n"
        "Chunks of %d bytes of %d bits = %d bits\n",
        fn,
        CHUNK_CHAR, CHAR_BIT, CHUNK_CHAR * CHAR_BIT);

    if (CHUNK_BITS != CHUNK_CHAR * CHAR_BIT) {
        fprintf(stdout,
        "%d bits chunk requested. Won't fit, trunkated to\n"
            "%d * %d = %d\n"
        "%d bits short.\n\n",
        CHUNK_BITS,
        CHUNK_CHAR, CHAR_BIT, CHUNK_BITS / CHAR_BIT * CHAR_BIT,
        CHUNK_BITS - CHUNK_CHAR * CHAR_BIT);
    }

    while ((k = fread(buffer, 1, CHUNK_CHAR, fh)) == CHUNK_CHAR) {
        prnt_cshex(p, CHUNK_CHAR, HEX_WIDTH);   /* Print as hex */
        printf("  ");
        prnt_csbytes(p, CHUNK_CHAR);        /* Print as text */
        putchar('\n');
    }

    if (!feof(fh)) {
        fprintf(stderr, " * Never reached EOF;\n");
        goto fail_close;
    }

    /* If input file does not fit in to CHUNK, report this */    
    if (k > 0) {
        printf("%d byte tail: '", k);
        prnt_csbytes(p, k);
        printf("'\n");
    }

    fclose(fh);

    return 0;
fail_close:
    fclose(fh);
fail_1:
    return 1;
}

Upvotes: 1

Richard J. Ross III
Richard J. Ross III

Reputation: 55533

strtoull() Converts a string that is in hex format (e.g. 0xFFAABBEE) to it's integer format.

What you really need is a function to convert a string to a hex string, like this:

char *strToHex(const char *input)
{
    char *output = calloc(1, strlen(input) * 3 + 1);

    char *o = output;
    int i = 0;

    for (; input[i] != '\0'; o += 3, i++)
    {
        sprintf(o, "%.2X ", input[i]);
    }

    // don't forget to free output!
    return output;
}

Upvotes: 1

adelbertc
adelbertc

Reputation: 7320

You can try doing getchar() 8 times (each value returned by getchar is 1 byte = 8 bits) and then using atoh or something - I'm not even sure if atoh exists, but barring that do something like atoi followed by itoh.. or write your own function to convert it.

Upvotes: 0

asaelr
asaelr

Reputation: 5456

The function strtoull (with 16) converts HEX string to number, not ASCII char to HEX string.

To print a char in HEX form, you should do something like printf("%02x ",buffer[0]);

Upvotes: 1

thumbmunkeys
thumbmunkeys

Reputation: 20764

strtoull converts a number represented by a string into an unsigned long long. Your input to this function (eg. string "testing") makes no sense as it has to be a number.

   printf("%llu\n", strtoull("123")); // prints 123

To get the result you want, you have to print each character of the string like this:

   for(int i=0; i<8; i++)
       printf( "%02X ", (unsigned char) buffer[i]);

Upvotes: 1

Related Questions