MasterUZ MasterUZ - 4 months ago 21
C++ Question

How to read and write correctly unsigned __int8 array to binary file using filestream in C++

I'm using generator to make random sequence of unsigned __int8'ers, and then writing them to file using ofstream.write() with this method;

void CDataGenerator::GenerateRandom(std::string outputFileName, int length, bool UseEntireRange, int max) {
std::ofstream file;
file.open(outputFileName, std::ifstream::out | std::ifstream::binary);
int count = 0;
unsigned __int8* buf = new unsigned __int8[length];
while (count < length-1) {
int number = 0;
if (UseEntireRange)
number = rand();
else {
int rnd = rand();
number = (int)((double)rnd / RAND_MAX * max);
}
int capacity = 0;
if (number == 0)
capacity = 1;
else
capacity = (int)(floor(log10(number)) + 1);
for (int i = 0; i < capacity; ++i) {
if (count >= length - 2)
break;
buf[count] = ((unsigned __int8)(number / (int)pow(10, capacity - i - 1)));
number %= (int)pow(10, capacity - i - 1);
++count;
}
++count;
buf[count] = BCD_SEPARATOR;
}
file.write((__int8*)&buf[0], length);
delete[] buf;
file.close();
}


Where
const static unsigned __int8 BCD_SEPARATOR = 0xff;


Then I try to read file with following method

unsigned __int8* CModel::GetRawData(std::string inputFileName, int &length) {
std::ifstream file(inputFileName, std::ifstream::ate | std::ifstream::binary);
length = file.tellg();
file.close();
file.open(inputFileName, std::ifstream::in | std::ifstream::binary);
unsigned __int8* result = new unsigned __int8[length];
file.read((__int8*)&result[0], length);
file.close();
return result;
}


Im my test generater creates sequence like this
0x0 0xFF 0x5 0x6 0xFF 0x1 0x9 0xFF 0x8 0xFF
but from reading stream I get
0x0 0xCD 0x5 0x6 0xCD 0x1 0x9 0xCD 0x8 0xCD
sequence.
It's obvously that all 0xff are replaced with 0xcd. Is it connected with (__int8*) casts and How do I solve it?

Answer

With knowledge of the CRT debug heap (I'm just assuming you're using Visual Studio), it's a good guess that the 0xCD value is from uninitialized heap memory. The question then becomes: Why are you seeing this in the output? To figure out why, you can simply step through your GenerateRandom function using your debugger/read the code.

From this it becomes apparent where the problem is:

for (int i = 0; i < capacity; ++i) {
    if (count >= length - 2)
        break;
    buf[count] = ((unsigned __int8)(number / (int)pow(10, capacity - i - 1)));
    number %= (int)pow(10, capacity - i - 1);
    ++count; //Increment count ONCE
}

++count; //Increment count a SECOND time
buf[count] = BCD_SEPARATOR;

The problem is that when the program leaves the for-loop shown here, count will already have been incremented once, so your "count" is already at your next uninitialized __int8 in the buffer. You then increment "count" once again before writing BCD_SEPARATOR to the "count" position in the buffer. This results in the program skipping the position where you actually want your BCD_SEPARATOR.

The next problem then becomes that, because you don't increment "count" between after you write the BCD_SEPARATOR to the buffer and the next time you enter the for-loop shown above, you immediately overwrite the BCD_SEPARATOR.

A solution to the problem could be to simply swap things around like this:

buf[count] = BCD_SEPARATOR;
++count;