Home > Enterprise >  Reading Hex from file, printf %x showing leading ff values
Reading Hex from file, printf %x showing leading ff values

Time:01-31

I have a file 'data.dat' with 26 hex byte values:

22 49 E1 09 62 18 42 8C 66 10 B0 11 84 9C 00 FF E0 40 1F F8 60 07 FE 2C 03 FF

I am trying to read these into c and print the hex values to the terminal with:

const int msgbuflen = 26;                
char message [msgbuflen];  

const char* filenameIn = "data.dat";  

FILE* fpIn = fopen(filenameIn, "rb");

if (!fpIn) {
  perror("ERROR: INPUT FILE CANNOT BE OPENED\n");
  exit(EXIT_FAILURE);
}

for (i=0; i<msgbuflen; i  ){  // clear the buffer
    message[i] = '0';
}

size_t ret_code = fread(message, sizeof(unsigned char), msgbuflen, fpIn);
for(int i = 0; i < msgbuflen; i  ){
  printf("message[%d] : 0x%x\n", i, message[i]);
}

fclose (fpIn);

When run the output for some of the bytes have 3 leading ff values:

message[0] : 0x22
message[1] : 0x49
message[2] : 0xffffffe1
message[3] : 0x9
message[4] : 0x62
message[5] : 0x18
message[6] : 0x42
message[7] : 0xffffff8c
message[8] : 0x66
message[9] : 0x10
message[10] : 0xffffffb0
message[11] : 0x11
message[12] : 0xffffff84
message[13] : 0xffffff9c
message[14] : 0x0
message[15] : 0xffffffff
message[16] : 0xffffffe0
message[17] : 0x40
message[18] : 0x1f
message[19] : 0xfffffff8
message[20] : 0x60
message[21] : 0x7
message[22] : 0xfffffffe
message[23] : 0x2c
message[24] : 0x3
message[25] : 0xffffffff

Why do these leading f's occur? for example message[2] : 0xffffffe1

I have tried formatting the output of the printf hex %x with 0xx but it makes no difference to the terminal output. Checking the sizeof each element in the char array, they are still 1 byte as expected:

printf("sizeof(message[2]) : %ld\n", sizeof(message[2]) );

%> sizeof(message[2]) : 1

I am now wondering if this is a formatting problem? There does not appear to be any more than 1 byte in each message element (as expected).

Using std::hex with cout produces the same issue.

CodePudding user response:

This is because char is a signed integer so 0xFF becomes -1. As %x prints an int, which is 32-bits, it becomes 0xFFFFFFFF which is a 32-bit negative one.

If you store as an unsigned char you will not have this problem

#include <cstdio>
int main() {
    unsigned char message[] = {0x22, 0x49, 0xE1, 0x09, 0x62, 0x18, 0x42, 0x8C, 0x66, 0x10, 0xB0, 0x11, 0x84, 0x9C, 0x00, 0xFF, 0xE0, 0x40, 0x1F, 0xF8, 0x60, 0x07, 0xFE, 0x2C, 0x03, 0xFF }; 
    int msgbuflen = sizeof(message)/sizeof(message[0]);
    for(int i = 0; i < msgbuflen; i  ){
        printf("message[%d] : 0x%x\n", i, message[i]);
    }
}

Produces

Program returned: 0
message[0] : 0x22
message[1] : 0x49
message[2] : 0xe1
message[3] : 0x9
message[4] : 0x62
message[5] : 0x18
message[6] : 0x42
message[7] : 0x8c
message[8] : 0x66
message[9] : 0x10
message[10] : 0xb0
message[11] : 0x11
message[12] : 0x84
message[13] : 0x9c
message[14] : 0x0
message[15] : 0xff
message[16] : 0xe0
message[17] : 0x40
message[18] : 0x1f
message[19] : 0xf8
message[20] : 0x60
message[21] : 0x7
message[22] : 0xfe
message[23] : 0x2c
message[24] : 0x3
message[25] : 0xff

Godbolt: https://godbolt.org/z/oxPqnYqMq

Alternatively you can just cast

    for(int i = 0; i < msgbuflen; i  ){
        printf("message[%d] : 0x%x\n", i, (unsigned char)message[i]);
    }

CodePudding user response:

Or in somewhat more current C (not using "C" style arrays) and a well defined unsigned 8 bits datatype. (char can be either signed or unsigned depending on the platform you are working on)

#include <array>
#include <iostream>
#include <sstream>
#include <cstdint>
#include <format>

// simulated opening of a std::ifstream
auto open_file()
{
    static std::array<std::uint8_t,26> data
    { 
        0x22, 0x49, 0xE1, 0x09, 0x62, 0x18, 0x42, 0x8C, 0x66, 0x10, 
        0xB0, 0x11, 0x84, 0x9C, 0x00, 0xFF, 0xE0, 0x40, 0x1F, 0xF8, 
        0x60, 0x07, 0xFE, 0x2C, 0x03, 0xFF
    };

    std::istringstream is(std::string{ data.begin(), data.end() });
    return is;
}

int main()
{
    std::array<std::uint8_t, 26> buffer;

    auto is = open_file();
    
    // get doesn't have an overload for std::uint8_t*
    // so check if sizeof char matches sizeof std::uint8_t
    static_assert(sizeof(char) == sizeof(std::uint8_t));
    is.get(reinterpret_cast<char*>(buffer.data()), buffer.size());

    for (const auto byte : buffer)
    {
        std::cout << std::format("0x{:x} ", byte);
    }

    return 0;
}
  • Related