Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write array into the binary file?

Tags:

c

file-io

I need some help - next piece of code writes a long double dynamic array into the file

int nx = 10, ny = 10;
long double **data = new long double *[nx]; 
long double **data_read = new long double *[nx]; 
for (int i = 0; i < nx; i++) {
    data[i] = new long double [ny];
    data_read[i] = new long double [ny];
}

data[4][4] = 10.0;
printf("%LF\n", data[4][4]);

FILE *file = fopen("data", "wb");
fwrite(data, nx * ny * sizeof(data), 1, file);
fclose(file);

file = fopen("data", "rb");
fread(data, nx * ny * sizeof(data_read), 1, file );
fclose(file);

printf("%LF\n", data_read[4][4]);

But data[4][4] != data_read[4][4], because after reading from file data_read[4][4]=0.0.

Anybody knows what am I doing wrong?

like image 927
erthalion Avatar asked Jan 19 '14 05:01

erthalion


People also ask

How do you write data into a binary file?

Until the end of the text file is reached perform the following steps: Read a line from the input text file, into 3 variables using fscanf(). The structure variable is set to values for elements(to write the structure variable into the output file. Write that structure variable to the output binary file using fwrite().

How do you write binary in C++?

To write a binary file in C++ use write method. It is used to write a given number of bytes on the given stream, starting at the position of the "put" pointer. The file is extended if the put pointer is currently at the end of the file.

What is a binary file example?

Binary files can be used to store any data; for example, a JPEG image is a binary file designed to be read by a computer system. The data inside a binary file is stored as raw bytes, which is not human readable.


2 Answers

You need to write each row in your pointer array individually. A mass write will not work for pointer-to-pointer implementations of a fake 2D array (or nD).

For writing:

for (int i=0; i<nx; ++i)
    fwrite(data[i], sizeof(data[i][0]), ny, file);

For reading:

for (int i=0; i<nx; ++i)
    fread(data[i], sizeof(data[i][0]), ny, file);

Frankly, you're (un)fortunate the process didn't crash outright, as you were writing a bunch of memory addresses to your disk file (which a hex dump would have showed you), and were likely walking off the end of your pointer-array allocation during both operations.

That said, I'd start learning about the standard C++ IO library rather than using C-code in a C++ world (or fix the tag on this question).


Single Block Write/Read

You asked if it is possible to do this as a single block read/write. The answer is yes, but you must allocate the memory contiguously. If you still want a pointer-to-pointer array you can certainly use one. Though I recommend using std::vector<long double> for the data buffer, the following will demonstrate what I refer to:

int main()
{
    int nx = 10, ny = 10;

    long double *buff1 = new long double[nx * ny];
    long double *buff2 = new long double[nx * ny];

    long double **data = new long double *[nx];
    long double **data_read = new long double *[nx];

    for (int i = 0; i < nx; i++)
    {
        data[i] = buff1 + (i*ny);
        data_read[i] = buff2 + (i*ny);
    }

    data[4][4] = 10.0;
    printf("%LF\n", data[4][4]);

    FILE *file = fopen("data.bin", "wb");
    fwrite(buff1, sizeof(*buff1), nx * ny, file);
    fclose(file);

    file = fopen("data.bin", "rb");
    fread(buff2, sizeof(*buff2), nx * ny, file );
    fclose(file);

    printf("%LF\n", data_read[4][4]);

    // delete pointer arrays
    delete [] data;
    delete [] data_read;

    // delete buffers
    delete [] buff1;
    delete [] buff2;
}

Output

10.000000
10.000000

Using a std::vector<> for an RAII Solution

All those allocations can get messy, and frankly prone to problems. Consider how this is different:

#include <iostream>
#include <fstream>
#include <vector>

int main()
{
    int nx = 10, ny = 10;

    // buffers for allocation
    std::vector<long double> buff1(nx*ny);
    std::vector<long double> buff2(nx*ny);

    // holds pointers into original
    std::vector<long double*> data(nx);
    std::vector<long double*> data_read(nx);

    for (int i = 0; i < nx; i++)
    {
        data[i] = buff1.data() + (i*ny);
        data_read[i] = buff2.data() + (i*ny);
    }

    data[4][4] = 10.0;
    std::cout << data[4][4] << std::endl;

    std::ofstream ofp("data.bin", std::ios::out | std::ios::binary);
    ofp.write(reinterpret_cast<const char*>(buff1.data()), buff1.size() * sizeof(buff1[0]));
    ofp.close();

    std::ifstream ifp("data.bin", std::ios::in | std::ios::binary);
    ifp.read(reinterpret_cast<char*>(buff2.data()), buff2.size() * sizeof(buff2[0]));
    ifp.close();

    std::cout << data_read[4][4] << std::endl;

    return 0;
}
like image 61
WhozCraig Avatar answered Sep 19 '22 00:09

WhozCraig


Change your code to (see comments for details):

...
data[4][4] = 10.0;
printf("%Lf\n", data[4][4]); // use %Lf to print long double, not %LF

FILE *file = fopen("data", "wb"); 
for (int i=0; i<nx; ++i) // must write row-by-row as data are not continuous
    fwrite(data[i], sizeof(long double), ny, file); 
      // cannot use sizeof(data) here as data is a pointer here, will always return 4
fclose(file);

file = fopen("data", "rb");
for (int i=0; i<nx; ++i)  // read row-by-row
    fread(data_read[i], sizeof(long double), ny, file); 
      // 1. read to data_read, not data
      // 2. cannot use sizeof(data) here as data is a pointer here, will always return 4
fclose(file);

printf("%Lf\n", data_read[4][4]);  // use %Lf to print long double, not %LF
...

Edit:

If you want to format the data in a continuous memory, use vector<long double> or long double data[nx*ny] instead. Then you can easily write or read by:

fwrite(data, nx * ny * sizeof(long double), 1, file);
...
fread(data_read, nx * ny * sizeof(long double), 1, file );
like image 21
herohuyongtao Avatar answered Sep 19 '22 00:09

herohuyongtao