0

I have successfully followed the answer posted here to write a structure (of type image_info_t) to a file. I repeat the process in a loop for N number of image_info_t's and all the data is serialized and added to the file correctly.

I now need to read the file, but I need to be able to read an arbitrary number, M, image_info_t structs to read from the file (all in order). The answer referenced above explicitly hardcodes the number of structures to read back from the file (i.e., student_t master[3];). However, I need this number to be dynamic.

I have read here that "C++ standard requires that arrays use either an integer literal or a integer constant when declaring its size. Use <vector> instead"

My question is: how can I do this? How can I read the set of image_info_t structs back from the file into a std::vector?

Here is my current (non-working) code that I am using to read the image_info_t data back from the file.

 std::ifstream input_file(path, std::ios::binary);
 const int kpts_size = kpts.size();
 feature_t master[kpts_size];  //DOES NOT WORK. If I change to `feature_t master[10];` it works.
 input_file.read((char*)&master, sizeof(master));
 input_file.close();

Note: this is not an access violation question, and is not related to the "Possible dup" answer. When you tag it as such, people stop reading my question which certainly doesn't help anyone.

Community
  • 1
  • 1
Brett
  • 11,637
  • 34
  • 127
  • 213
  • Easy, search StackOverflow for "c++ parse read file". – Thomas Matthews Feb 01 '14 at 19:38
  • Why is this tagged C? – Ed S. Feb 01 '14 at 19:47
  • Becuase if I can do it in C somehow, that would be fine too. – Brett Feb 01 '14 at 19:47
  • You need to show the definitions for your names, e.g. `kpts`, but what's biting you is the difference between a compile-time constant and a `const` qualifier. C++11 has `constexpr` to tell the compiler the value can be calculated during compilation, but that declaration needs to have been applied to every non-literal_constant in the calculation, in this case `kpts`'s member function `size()`, which I'm betting it hasn't. – jthill Feb 01 '14 at 19:49
  • It can be done in either. Which solution do you want? – Ed S. Feb 01 '14 at 19:56
  • @EdS. I suppose C++. But it's not critical. – Brett Feb 01 '14 at 19:58
  • http://stackoverflow.com/questions/3438132/serialise-and-deserialise-vector-in-binary – bobah Feb 01 '14 at 20:53

2 Answers2

2
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>


//Some kind of structure containing many sample data types..
typedef struct image_info
{
    char image_type;
    std::uint32_t md5_hash;
    std::string image_name;
    std::vector<std::uint8_t> bytes;
} image_info_t;


//Used for writing the above structure to a stream..
std::ostream& operator << (std::ostream& os, const image_info &entry)
{
    std::size_t image_name_size = entry.image_name.size();
    std::size_t image_bytes_size = entry.bytes.size();

    os.write(&entry.image_type, sizeof(entry.image_type));
    os.write(reinterpret_cast<const char*>(&entry.md5_hash), sizeof(entry.md5_hash));
    os.write(reinterpret_cast<const char*>(&image_name_size), sizeof(image_name_size));
    os.write(entry.image_name.c_str(), entry.image_name.size());
    os.write(reinterpret_cast<const char*>(&image_bytes_size), sizeof(image_bytes_size));
    os.write(reinterpret_cast<const char*>(&entry.bytes[0]), entry.bytes.size());
    return os;
}

//Used for reading the above structure from a stream..
std::istream& operator >> (std::istream& is, image_info &entry)
{
    std::size_t image_name_size = 0;
    std::size_t image_bytes_size = 0;

    is.read(&entry.image_type, sizeof(entry.image_type));
    is.read(reinterpret_cast<char*>(&entry.md5_hash), sizeof(entry.md5_hash));
    is.read(reinterpret_cast<char*>(&image_name_size), sizeof(image_name_size));

    entry.image_name.resize(image_name_size);
    is.read(&entry.image_name[0], image_name_size);
    is.read(reinterpret_cast<char*>(&image_bytes_size), sizeof(image_bytes_size));

    entry.bytes.resize(image_bytes_size);
    is.read(reinterpret_cast<char*>(&entry.bytes[0]), image_bytes_size);
    return is;
}

//Used for writing an array/vector of the above structure to a stream..
std::ostream& operator << (std::ostream& os, const std::vector<image_info> &entry)
{
    std::size_t entry_size = entry.size();
    os.write(reinterpret_cast<const char*>(&entry_size), sizeof(entry_size));
    for (std::size_t i = 0; i < entry_size; ++i)
        os << entry[i];

    return os;
}

//Used for reading an array/vector of the above structure from a stream..
std::istream& operator >> (std::istream& is, std::vector<image_info> &entry)
{
    std::size_t entry_size = 0;
    is.read(reinterpret_cast<char*>(&entry_size), sizeof(entry_size));
    entry.resize(entry_size);
    for (std::size_t i = 0; i < entry_size; ++i)
        is >> entry[i];
    return is;
}

int main()
{
    std::vector<image_info_t> outdata;
    std::vector<image_info_t> indata;
    image_info_t one;
    image_info_t two;

    one.image_name = "one";
    one.image_type = 'a';
    one.md5_hash = 1;
    one.bytes.push_back(0);

    two.image_name = "two";
    two.image_type = 'b';
    two.md5_hash = 2;
    two.bytes.push_back(1);

    outdata.push_back(one);
    outdata.push_back(two);

    std::fstream out("C:/Users/School/Desktop/Image_Info_T.bin", std::ios::out | std::ios::binary);

    if (out.is_open())
    {
        out << outdata;
        out.close();
    }

    std::fstream in("C:/Users/School/Desktop/Image_Info_T.bin", std::ios::in | std::ios::binary);

    if (in.is_open())
    {
        in >> indata;
    }

    std::cout<<indata[0].image_name<<"    "<<indata[1].image_name;
}
Brandon
  • 22,723
  • 11
  • 93
  • 186
  • this looks really promising. I'm getting a two compile errors: (a) at the line `os << *it;` stating `Invalid operands to binary expression ('std::istream') and 'value_type' (aka image image_info)`. Also (2) the line `os << *it;` states: `Invalid operand binary expression ('std::ostream') and 'const image_info'`. Any ideas?? – Brett Feb 01 '14 at 21:52
  • You should not have had any errors.. It compiles and runs. Anyway, I removed the iterators and instead used the index operator. It should work for you now. – Brandon Feb 01 '14 at 21:56
  • It runs indeed! Not sure where the difference was. – Brett Feb 01 '14 at 21:57
0

If you want to avoid using a vector, you can initialize your array by doing the following:

feature_t* master = new feature_t[kpts.size()];
//code
delete[] master;

Alternatively, with a vector you can simply make a vector of feature_t, IE:

std::vector<feature_t> master;

Generally I find the easiest way to add structs or classes to a vector is to make an instance of them, then fill all the values and add it to the vector, so I might do:

feature_t temp;
while (getline(file, str))
{
    temp.a = ...;
    temp.b = ...;
    master.push_back(temp);
}

In C, new would be replaced with malloc (or one of its derivative functions), so you would use:

feature_t* master = malloc(sizeof(master) * kpts.size());
//code
free(master);
IllusiveBrian
  • 3,105
  • 2
  • 14
  • 17