0

I have a binary file. i am reading 16 bytes at a time it using fstream.

I want to convert it to an integer. I tried atoi. but it didnt work. In python we can do that by converting to byte stream using stringobtained.encode('utf-8') and then converting it to int using int(bytestring.hex(),16). Should we follow such an elloborate steps as done in python or is there a way to convert it directly?

ifstream file(binfile, ios::in | ios::binary | ios::ate);
if (file.is_open())
{

    size = file.tellg();
    memblock = new char[size];
    file.seekg(0, ios::beg);
    while (!file.eof())
    {
        file.read(memblock, 16);            
        int a = atoi(memblock); // doesnt work 0 always
        cout << a << "\n";
        memset(memblock, 0, sizeof(memblock));
    }
    file.close();

Edit:

This is the sample contents of the file.

53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00
04 00 01 01 00 40 20 20 00 00 05 A3 00 00 00 47
00 00 00 2E 00 00 00 3B 00 00 00 04 00 00 00 01

I need to read it as 16 byte i.e. 32 hex digits at a time.(i.e. one row in the sample file content) and convert it to integer. so when reading 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00, i should get, 110748049513798795666017677735771517696

But i couldnt do it. I always get 0 even after trying strtoull. Am i reading the file wrong, or what am i missing.

user00011
  • 103
  • 1
  • 15
  • 4
    `atoi()` converts a ***null terminated text string*** to an integer. Unless your 16 bytes all have the last byte set to `'\0'`, and the previous fifteen bytes consist of leading whitespace, an optional minus sign, and at least one more character between '0' and '9', then `atoi()` will not work, because that's what it does, and the only thing that it does (technically, the '\0' doesn't have to be the last byte, but that's not a relevant detail). – Sam Varshavchik Jan 07 '19 at 16:13
  • 1
    An `int` is typically 4 bytes, which can't store a 16 byte value. – NathanOliver Jan 07 '19 at 16:14
  • Like this? https://stackoverflow.com/q/1070497 – Robert Harvey Jan 07 '19 at 16:14
  • @NathanOliver: Presumably it's 16 hex digits. An eight-byte int would do it. – Robert Harvey Jan 07 '19 at 16:18
  • Ok, so it's sixteen hex nybbles. Copy the 16 bytes into a 17 byte `char` array, slap on a `'\0'` at the end of it, and feed it to `strtoull()`. The End. – Sam Varshavchik Jan 07 '19 at 16:20
  • 1
    `size = file.tellg();` I believe this should return 0 since the file was just opened. Also, `sizeof(memblock)` will return the size of the pointer, not the length of the buffer. – 001 Jan 07 '19 at 16:21
  • What is the relationship between the bytes in the file and the integer value? That is, what should the code do to "convert" the bytes to an integer value? – Pete Becker Jan 07 '19 at 16:24
  • Please show the actual file data, and what the resulting integer should look like – Remy Lebeau Jan 07 '19 at 16:45
  • 1
    @JohnnyMopp I though the same thing until I looked up what `ios::ate` does. That part of the code is fine. – Mark Ransom Jan 08 '19 at 04:23
  • @RobertHarvey if you look at the Python code it really is converting to a 16-byte integer albeit in an awkward way: `.hex()` converts a 16 byte string to 32 hex digits. Python integers are unbounded; there's no suitable C++ type for this problem. – Mark Ransom Jan 08 '19 at 04:28

2 Answers2

1

You have a number of problems here. First is that C++ doesn't have a standard 128-bit integer type. You may be able to find a compiler extension, see for example Is there a 128 bit integer in gcc? or Is there a 128 bit integer in C++?.

Second is that you're trying to decode raw bytes instead of a character string. atoi will stop at the first non-digit character it runs into, which 246 times out of 256 will be the very first byte, thus it returns zero. If you're very unlucky you will read 16 valid digits and atoi will start reading uninitialized memory, leading to undefined behavior.

You don't need atoi anyway, your problem is much simpler than that. You just need to assemble 16 bytes into an integer, which can be done with shifting and or operators. The only complication is that read wants a char type which will probably be signed, and you need unsigned bytes.

ifstream file(binfile, ios::in | ios::binary);
char memblock[16];
while (file.read(memblock, 16))
{
    uint128_t a = 0;
    for (int i = 0; i < 16; ++i)
    {
        a = (a << 8) | (static_cast<unsigned int>(memblock[i]) & 0xff);
    }
    cout << a << "\n";
}
file.close();
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Thanks for the idea. But neither uint128_t not unsigned __int128 worked for me. As i am using MS VSC++ compiler. But i used you way and read 4 bytes at a time and stored in a string used bitset to concatenate those strings to form 128bit binary equivalent of the 16byte hex value. As of now i am using that value and right shifting , so i need not store the resultant 128 bit binary string. But any idea on how to use unsigned __128 in vsc++? It would save a lot of time – user00011 Jan 09 '19 at 17:42
  • @Prakrithi according to [this](https://stackoverflow.com/q/6759592/5987) there is no way with VSC++ at this time. I just did a search through the libraries in VS2017 and can confirm. – Mark Ransom Jan 09 '19 at 19:03
0

It the number is binary what you want is:

    short value ;
    file.read(&value, sizeof (value));            

Depending upon how the file was written and your processor, you may have to reverse the bytes in value using bit operations.

user3344003
  • 20,574
  • 3
  • 26
  • 62