help with i/o binary

hello everybody
i'm trying to write/read some simple binary files in my aplication. i'm able to do it in text files, but binary is being a pain.

i have some ints, bools and enums that i want to write to a file and read later.
but according to the reference on this site, the function write() asks for a "pointer to char". how am i supposed to do that with an int?

and then i saw this article, which made me even more confused:
http://www.cplusplus.com/articles/DzywvCM9/
where does he gets "u16" from??

and then i saw this video, where he uses fopen, fwrite, fclose, to write a whole struct.
http://www.youtube.com/watch?v=P5_ED_JuGls


so, i simply can't get any more confused. how to write and read some simple ints to/from a binary file?
thanks!
This example creates a file named "example.dat", stores some integers in it, then reads them back.

The issue of the read/write function requiring a "pointer to char" is dealt with by means of a cast, where the pointer to the data is reinterpreted as a pointer to a character. I've used two different styles of cast here, because you are likely to come across both.

I also used the sizeof() operator in two different ways, with either the name of the object, or the name of the type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <fstream>

using namespace std;

void createFile(const char * filename);

int main()
{
    char fname[] = "example.dat";
    
    // create the data file
    createFile(fname);

    // now read the contents of the file
    ifstream fin(fname, ios::binary);
    
    int n;
    while (fin.read(reinterpret_cast<char *>(&n), sizeof(int)))    
        cout << " n = " << n << endl;    

    return 0;
}

void createFile(const char * filename)
{
    ofstream fout(filename, ios::binary);
    
    int a = 7654321;
    int b = -9876;
    int c = 123;
    
    fout.write((char *) &a, sizeof(a));
    fout.write((char *) &b, sizeof(b));
    fout.write((char *) &c, sizeof(c));
}

Output:
 n = 7654321
 n = -9876
 n = 123

thanks! finally, its clarifying! :)

but if i use this approach, won't i have the problem pointed in the article (int might be 8 bytes on one machine, but only 4 bytes on another) at the time of reading?
i'd like to know where does he take u16 and u32 from. i don't see a definition of that..
u16 was just a personal typedef I use. It's kind of out of context of that article.

Instead of u16 you can us uint16_t from the <cstdint> header.
oh, the author is here!:P

i see. so i can use uint16_t for both ints, chars and bools, if i cast them? (altough i think it would be overkill using uint16 for char and bool)

EDIT: oh wait, that does not make any sense. i'll try to construct the program and i'll be back in a while.
Last edited on
WOW, i did a mess here. but it's getting funny, really :)
this is what i made. i think i'm missing something:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char* block;
const size_t total_values = 25;
std::vector <uint16_t> values;
values.resize(total_values);
std::ofstream settings_file("save/settings.data", std::ios::binary);
if (settings_file.is_open()){
    for(size_t i=0; i<=total_values; i++){
        values[i] = i;
        block = reinterpret_cast<char*>(&values[i]);
        settings_file.write(block, sizeof(uint16_t));
        std::cout << "\nvalues[i]:" << values[i];
        std::cout << "\nblock:" << block;
    }
}
settings_file.close();

does that makes any sense? it created a file called "settings.data", with a size of nearly 52 bytes.


EDIT: Yeaaaahh, IT'S ALIIIVEEE
i mean, its working!
this is what i finished up with:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void saveSettings(){
    char* block;
    const size_t total_values = 10;
    std::vector <uint16_t> values;
    values.resize(total_values);
    std::ofstream settings_file("settings.dat", std::ios::binary);
    if (settings_file.is_open()){
        for(size_t i=0; i<total_values; i++){
            values[i] = i;
            block = reinterpret_cast<char*>(&values[i]);
            settings_file.write(block, sizeof(uint16_t));
            std::cout << "\nvalues[i]:" << values[i];
            std::cout << "\nblock:" << block;
        }
    }
    settings_file.close();
}

void loadSettings(){
    uint16_t n;
    std::ifstream settings_file("settings.dat", std::ios::binary);
    while (settings_file.read(reinterpret_cast<char *>(&n), sizeof(uint16_t))){
        std::cout << "\nn:" << n << ", n*2:" << n*2;
    }
    settings_file.close();
}

int main(){
    saveSettings();
    loadSettings();
    return 0;
}


and the output:
n:0, nx2:0
n:1, n*2:2
n:2, n*2:4
n:3, n*2:6
n:4, n*2:8
n:5, n*2:10
n:6, n*2:12
n:7, n*2:14
n:8, n*2:16
n:9, n*2:18


i made the multiplications just to make sure it's everything right :)
now PLEASE, if i did something wrong or could do better, please let me know.
thanks Chervil and Disch, you helped me a lot!
Last edited on
That seems to be mostly along the right lines. Though it does (or did) contain an error which caused an access violation when I tried to run it.
for(size_t i=0; i<=total_values; i++)
(the condition should be i<total_values - I think you edited the code to fix this.

I wasn't entirely sure why you were using a vector at all, since you could create the file just as well without it. Also, when printing the value of block you may have intended to print the address rather than the default behaviour which is to treat it as a character string.

Here's my version, which is only very slightly different, and any changes I've made are 99% down to personal preference.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void saveSettings()
{
    const size_t total_values = 10;
    
    std::vector <uint16_t> values(total_values);
    for (size_t i=0; i<total_values; i++)
    {
        values[i] = i*i;
    }
    
    char* block;
    std::ofstream settings_file("settings.dat", std::ios::binary);

    if (settings_file.is_open())
    {
        for (size_t i=0; i<total_values; i++)
        {
            block = reinterpret_cast<char*>(&values[i]);
            settings_file.write(block, sizeof(uint16_t));
            std::cout << " values[i]: " << values[i];
            std::cout << "   block: " << reinterpret_cast<void *> (block) << std::endl;
        }
    }
    settings_file.close();
}

void loadSettings()
{
    uint16_t n;
    std::ifstream settings_file("settings.dat", std::ios::binary);
    while (settings_file.read(reinterpret_cast<char *>(&n), sizeof(uint16_t)))
    {
        std::cout << " n: " << n << std::endl;
    }
    settings_file.close();
}

int main()
{
    saveSettings();
    std::cout << "\n\n\n";
    loadSettings();
    return 0;
}

Output:
 values[i]: 0   block: 0x3e3c90
 values[i]: 1   block: 0x3e3c92
 values[i]: 4   block: 0x3e3c94
 values[i]: 9   block: 0x3e3c96
 values[i]: 16   block: 0x3e3c98
 values[i]: 25   block: 0x3e3c9a
 values[i]: 36   block: 0x3e3c9c
 values[i]: 49   block: 0x3e3c9e
 values[i]: 64   block: 0x3e3ca0
 values[i]: 81   block: 0x3e3ca2



 n: 0
 n: 1
 n: 4
 n: 9
 n: 16
 n: 25
 n: 36
 n: 49
 n: 64
 n: 81

Last edited on
yeah, i edited that error :P
and thanks, i really intended to print the adresses.

just one more thing: how can i convert from bool to char using the same approach? i mean, a fixed amount of memory. do i have to convert it to uint16_t (or maybe uint8_t?) and then convert it again to char?
would it be something like
1
2
3
4
bool bool_value = true;
uint8_t = static_cast<uint8_t> (bool_value);
block = reinterpret_cast<char*>(&bool_value);
settings_file.write(block, sizeof(uint8_t));


does not seems that good to me :P


and about string, i do not really need to make conversions, right? just the common conversion from char to string and vice-versa?
Last edited on
oh, wait.
both char and bool ocupy a constant size in memory (1 byte, i suppose) in all systems? if so, i don't need to convert it to uint, right?

ok, then i tried this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    std::vector <int> number_values = {50, 60, 100, 200};
    std::vector <bool> bool_values = {true, true, false, true};
    size_t numbers_total = number_values.size();
    size_t bools_total = bool_values.size();
    std::ofstream settings_file("save/settings.dat", std::ios::binary);
    if (settings_file.is_open()){
        //Numbers
        for(size_t i=0; i<numbers_total; i++){
            settings_file.write((char*) &number_values[i], sizeof(uint16_t));
            std::cout << "\nnumber_values[i]:" << number_values[i];
            std::cout << "; block:" << (void*)&number_values[i] << std::endl;
        }
        //Bools
        for(size_t i=0; i<bools_total; i++){
            settings_file.write((char*) &bool_values[i], sizeof(uint8_t));
            std::cout << "\nbool_values[i]:" << bool_values[i];
            std::cout << "; block:" << (void*)&bool_values[i] << std::endl;
        }
    }


but i get an error at line 15:
error: taking address of temporary [-fpermissive]|


i don't get it. is it really temporary? if so, why don't the line 9 returns the same error?

thanks!



EDIT:
after a while i discovered: i can't use vector<bool> for this, as the vector stores each bool in a single bite, instead of a byte. so, i changed to std::deque <bool> bool_values = {true, true, false, true};
Last edited on
I'm not sure why the two cases behave differently. I tried this in two different compilers, one worked ok, the other gave this same error. I can guess at some reasons, but that isn't really helpful.

Meanwhile, if you are really sure that the data item occupies just a single byte, then you could replace this:
 
    settings_file.write((char*) &bool_values[i], sizeof(uint8_t));

with this:
 
    settings_file.put(bool_values[i]);


Ah, I think I understand now. vector<bool> is a special case.
http://www.cplusplus.com/reference/vector/vector-bool/
Quote:
The storage is not necessarily an array of bool values, but the library implementation may optimize storage so that each value is stored in a single bit.
Last edited on
I was playing with the above code, and noticed a discrepancy.
 
std::vector <int> number_values = {50, 60, 100, 200};

 
settings_file.write((char*) &number_values[i], sizeof(uint16_t));

Notice the vector is declared with type int but the data is written to the file as a type uint16_t.

This is significant, as the two are not necessarily the same length. It should be:
 
std::vector <uint16_t> number_values = {50, 60, 100, 200};
thanks Chervil. actually i'm too newbie to be sure of anything in programming, so i think i'll keep it as it is.

by the way, i edited my last post a minute before you answered. it seems that vector<bool> is a specialized type for storing bools in a single bit.
http://www.cplusplus.com/reference/vector/vector-bool/

but thank you for your help! now it's working perfect! i'm saving chars, bools, ints and some enums to binary file and sucessfully loading everything :)
actually i'm too newbie to be sure of anything in programming, so i think i'll keep it as it is.


Keeping it as it is could lead to loss of data. You must make the the two consistent.
Last edited on
oh no, sorry, i was talking about your other post.
you're right, i changed it when trying to discover what was causing the error in bools vector. i fixed it, thanks for the note :)
Out of interest, I was looking at two other aspects of this. One, reading and writing the entire contents of the vector in a single instruction. Secondly, handling data of a variable length.

There may be flaws in this code, but i think its heading in the right direction
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <vector>
#include <cstdint>
#include <ctime>
#include <cstdlib>

void writeFile();
void readFile();

int main()
{
    srand(time(0));

    std::cout<< "\nWriting:" << std::endl;
    writeFile();

    std::cout<< "\nReading:" << std::endl;
    readFile();
    
    return 0;
}


void writeFile()
{
    std::ofstream settings_file("save\\settings.dat", std::ios::binary);
    if (!settings_file)
    {
        std::cout <<"settings file not open" << std::endl;
        return;    
    }

    size_t size;
    while ( (size = rand() % 20) && (size < 2) )
        { };
    std::cout << "size = " << size << std::endl;
            
    std::vector <uint16_t> number_values;
    for (size_t i=0; i<size; i++)
    {
        number_values.push_back(rand()%10000);
        std::cout << std::setw(3) << i << "  " << number_values[i] << std::endl;    
    }
    
    uint16_t data_count  = number_values.size();
    size_t   data_length = data_count * sizeof(uint16_t);
    
    settings_file.write((char*) &data_count, sizeof(uint16_t));    
    settings_file.write((char*) number_values.data() , data_length);
    
}

void readFile()
{
    std::ifstream settings_file("save\\settings.dat", std::ios::binary);
    if (!settings_file)
    {
        std::cout <<"settings file not open" << std::endl;
        return;    
    }
    
    uint16_t data_count;
    settings_file.read((char *) &data_count, sizeof(data_count)  );
    
    std::cout << "size = " << data_count << std::endl;
            
    std::vector <uint16_t> number_values(data_count);
    size_t   data_length = data_count * sizeof(uint16_t);    
    settings_file.read((char*) number_values.data() , data_length);
    
    for (size_t i=0; i<data_count; i++)
    {
        std::cout <<  std::setw(3) << i << "  " << number_values[i] <<std::endl;
    }
    

}


wow, that's even better. i'll keep it for reference :D
thanks again!
sorry for bumping this thread, but i have another question regarding this: how can i safely store floats and doubles? there is nothing like "ufloat16_t" (:P) so, how to store values that will be correctly read from different systems?

i did this, just out of curiosity:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
int main(){
    cout << "Char: " << sizeof(char);
    cout << "\nBool: " << sizeof(bool);
    cout << "\nShort Int: " << sizeof(short int);
    cout << "\nInt: " << sizeof(int);
    cout << "\nLong Int: " << sizeof(long int);
    cout << "\nLong Long Int: " << sizeof(long long int);
    cout << "\nFloat: " << sizeof(float);
    cout << "\nDouble: " << sizeof(double);

    cout << "\nUint8_t: " << sizeof(uint8_t);
    cout << "\nUint16_t: " << sizeof(uint16_t);
    cout << "\nUint32_t: " << sizeof(uint32_t);
    cout << "\nUint64_t: " << sizeof(uint64_t);

    return 0;
}

and got this:
Char: 1
Bool: 1
Short Int: 2
Int: 4
Long Int: 4
Long Long Int: 8
Float: 4
Double: 8
Uint8_t: 1
Uint16_t: 2
Uint32_t: 4
Uint64_t: 8


what would happen if i did this:
1
2
float test = 1.1111111111111111;
file.write((char*) &test, sizeof(uint16_t));

would i lose some precision numbers, or would i get an undefined number?
thanks!
You would only write [part of] the mantissa, you would lose sign, exponent, and the most significant bits of the mantissa:

http://en.wikipedia.org/wiki/Floating_point#Internal_representation


Basically you would be writing nonsense. The value would not be useful in any meaningful way.

EDIT (assuming of course, you're on a little endian system)
Last edited on
oh, i see. so, any hint on how to safely store floats across different systems?
Topic archived. No new replies allowed.