I really need your help. I have the following structs in my code:
struct Field{
char name[20];
int type;
int length;
};
struct Record{
vector<Field> structure;
vector<string> info;
};
What I want to do is to store a vector of my struct Record inside a binary file and to successfully load it back. The problem is that my struct has two vectors inside of it and they are causing me some trouble. Can you help me out?
You basically just write functions that would write the structure to a stream. First you write the size of the structure if it's a POD.
If it's not POD, you write the size of each element then you write the data for the element.
Below you can see for writevecfield, it writes the size of the vector first. Then it writes the entire POD structure to the stream. writesvecstring writes the size of the vector first. Then it goes through the vector and writes: the size of each string followed by its contents.
To read, you do the opposite. readvecfield reads the size of the vector from the file because it was the first thing written. After reading it, we resize the vector and read "size" amount of "Field" structures into the new vector.
To read the strings, we do the opposite as well. readvecstring reads the size of the vector from the file first. It resizes the vector to the read size. Next it loops "size" amount of times because that's the amount of strings in the file.
We then read the size of the string, resize a string and read the contents into that string. We then add that to the vector and move onto the next string: read the size first, resize a string, read contents, add to vector..
#include <fstream>
#include <vector>
#include <iostream>
#include <sstream>
using namespace std;
struct Field
{
char name[20];
int type;
int length;
};
struct Record
{
vector<Field> structure;
vector<string> info;
};
void writevecfield(ostream& os, const vector<Field> &vec)
{
typename vector<Field>::size_type size = vec.size();
os.write((char*)&size, sizeof(size));
os.write((char*)&vec[0], vec.size() * sizeof(Field));
}
void readvecfield(istream& is, vector<Field> &vec)
{
typename vector<Field>::size_type size = 0;
is.read((char*)&size, sizeof(size));
vec.resize(size);
is.read((char*)&vec[0], vec.size() * sizeof(Field));
}
void writevecstring(ostream& os, const vector<string> &vec)
{
typename vector<string>::size_type size = vec.size();
os.write((char*)&size, sizeof(size));
for (typename vector<string>::size_type i = 0; i < size; ++i)
{
typename vector<string>::size_type element_size = vec[i].size();
os.write((char*)&element_size, sizeof(element_size));
os.write(&vec[i][0], element_size);
}
}
void readvecstring(istream& is, vector<string> &vec)
{
typename vector<string>::size_type size = 0;
is.read((char*)&size, sizeof(size));
vec.resize(size);
for (typename vector<string>::size_type i = 0; i < size; ++i)
{
typename vector<string>::size_type element_size = 0;
is.read((char*)&element_size, sizeof(element_size));
vec[i].resize(element_size);
is.read(&vec[i][0], element_size);
}
}
void WriteRecord(ostream& out, const Record& r)
{
writevecfield(out, r.structure);
writevecstring(out, r.info);
}
void ReadRecord(istream& in, Record& r)
{
readvecfield(in, r.structure);
readvecstring(in, r.info);
}
int main()
{
Record R;
Field first = {"HELLO", 1, 20};
Field second = {"WORLD", 2, 40};
R.structure.push_back(first);
R.structure.push_back(second);
R.info.push_back("INFO FOR HELLO");
R.info.push_back("INFO FOR WORLD");
std::ofstream out("C:/Users/***/Desktop/Test.bin", std::ios::out | std::ios::binary);
WriteRecord(out, R);
out.close();
Record RR;
std::ifstream in("C:/Users/***/Desktop/Test.bin", std::ios::in | std::ios::binary);
ReadRecord(in, RR);
in.close();
for (int i = 0; i < RR.structure.size(); ++i)
{
std::cout<<"Name: "<<RR.structure[i].name<<"\n";
std::cout<<"Type: "<<RR.structure[i].type<<"\n";
std::cout<<"Length: "<<RR.structure[i].length<<"\n";
std::cout<<"INFO: "<<RR.info[i]<<"\n\n";
}
}
I used c-like sentence to process. The key is you just need to find the address of the first vec data, than use adjacent buffer to write them in files.
bool storeStructVec(FILE *fpOut, const vector<Field> &vec)
{
unsigned int nSize = vec.size();
if (nSize != fwrite(&vec[0],sizeof(Field),nSize,fpOut))
return false;
else return true;
}
Related
struct MyStruct {
Items item[100][60];
string Something;
int X;
int Y;
};
I have this struct "MyStruct" with a 2D Array of 100 * 60.
If I want to save the struct in Json Array for the item[100][60]
how I can do it using nlohmann json?
could anyone help me please?
Or if there is a way to save as binary file without using boost, I'll take that too.
void Save(std::string name, MyStruct test) {
std::string filename = name + ".dat";
std::ofstream out(filename);
boost::archive::binary_oarchive binary_output_archive(out);
binary_output_archive& test;
out.close();
}
void Read(std::string filename) {
std::ifstream in(filename + ".dat");
boost::archive::binary_iarchive binary_input_archive(in);
MyStruct test;
binary_input_archive& test;
in.close();
}
I tried this but it also crash sometimes so I want a better way
void Save(const std::string& name, const MyStruct& test) {
auto result = nlohmann::json{
{"item", json::array()},
{"Something", test.Something}
{"X", test.X},
{"Y", test.Y},
};
for (auto i = 0u; i < 100u; ++i) {
auto& outer = result["item"];
for (auto j = 0u; j < 60u; ++j) {
// You'll need to convert whatever Items is into a JSON type first
outer[i].push_back(test[i][j]);
}
}
auto out = std::ofstream out(name + ".dat");
out << result;
}
Something like this will suffice for saving, you can work out the deserialisation from this and the docs.
I strongly advise that you do not use Items item[100][60], the stack is not for items that large. Use a vector of std::arrays to use the heap but retain the same memory layout.
Hey I've a dynamic array and I want to load to this array the data of my Wav file, I already wrote the beginning but I can't figure it out how to load the file in my dynamic array, can somebody help me further with this code?
#include <iostream>
using namespace std;
template <typename T>
class Array{
public:
int size;
T *arr;
Array(int s){
size = s;
arr = new T[size];
}
T& operator[](int index)
{
if (index > size)
resize(index);
return arr[index];
}
void resize(int newSize) {
T* newArray = new T[newSize];
for (int i = 0; i <size; i++)
{
newArrayi] = arr[i];
}
delete[] arr;
arr = newArray;
size = newSize;
}
};
int main(){
Array<char> wavArray(10);
FILE *inputFile;
inputFile =fopen("song.wav", "rb");
return 0;
}
if you just want to load the complete file into memory, this may come in handy:
#include <iterator>
// a function to load everything from an istream into a std::vector<char>
std::vector<char> load_from_stream(std::istream& is) {
return {std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>()};
}
... and use the C++ file streaming classes to open and automatically close files.
{
// open the file
std::ifstream is(file, std::ios::binary);
// check if it's opened
if(is) {
// call the function to load all from the stream
auto content = load_from_stream(is);
// print what we got (works on textfiles)
std::copy(content.begin(), content.end(),
std::ostream_iterator<char>(std::cout));
} else {
std::cerr << "failed opening " << file << "\n";
}
}
... but a WAV file contains a lot of different chunks describing the contents of the file so you may want to create individual classes for streaming these chunks to and from files.
char* readFileBytes(const char *name)
{
FILE *fl = fopen(name, "r");
fseek(fl, 0, SEEK_END);
long len = ftell(fl);
char *ret = malloc(len);
fseek(fl, 0, SEEK_SET);
fread(ret, 1, len, fl);
fclose(fl);
return ret;
}
I have a class with 2 data members: size and an array of ints (dynamically allocated). The purpose of the class is to create an array of a size and fill it with values. The task is to create a constructor that takes a string as its parameter, but the string looks like this: "12|13|14|15" etc. I have searched this but all the solutions are a little too complicated, as they involve vectors and we haven't started with vectors yet. I basically want to put these numbers into the array of ints, 1 by 1 and also find out the size of the array. How can I do that? I tried messing with getline and stringstream but that gave me a load of errors. My code looks like this.
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
class IntArrays {
private:
static int objcount;
int size;
public:
int *arrayints;
const static int objcountf();
IntArrays(int);
IntArrays(const IntArrays &p){
size = p.size;
for (int i = 0;i <size;i++){
arrayints[i] = p.arrayints[i];
}
}
IntArrays(std::string f){
// ignore the other constructors, this is the constructor that is giving me trouble
int counter =0;
istringstream inStream(f);
string newstring;
while (getline(iss,newstring, '|')){
arrayints[counter] = stoi(newstring);
counter++;}
void enternums();
(note that this is only the header file, and that the current string constructor I have there does not work.
This code is my version. I prefer to use a vector rather than a raw array.
Class definition:
class IntArrays {
public:
IntArrays(const string&, const char&);
const vector<int>& data() { return _data; }
const int size() { return _data.size(); }
private:
vector<int> _data;
};
The following is the constructor implementation:
IntArrays::IntArrays(const string& str, const char& delimiter) {
string buff;
for(auto& n:str) {
if(n != delimiter) buff+=n; else
if(n == delimiter && buff != "") {
_data.push_back(stoi(buff));
buff = "";
}
}
if(buff != "") _data.push_back(stoi(buff));
}
And then we just use the class:
IntArrays a("1|4|9|6|69", '|');
vector<int> da = a.data();
IntArrays b("1,4,9,6,69", ',');
vector<int> db = b.data();
I will try to have a recursion for that =p
Sorry that I cannot provide a c++ version =p..
This is a java version i guess.
list parse(string data, list base) {
if (data.length > 0) {
string s = data.subStr(0,1);
if (s == "|") {
base.push(0); //set the initial value for a new value
} else {
int i = parseInt(s);
int result = base.pop()*10 + i; //recalculate the result
base.push(result);
}
return parse(data.subStr(1),base); //recursion
} else {
return base; //return the result
}
}
As Joachim pointed out, you do not initialize the pointer. Unfortunately, you do not have the size of the array before the allocation, so you are left with a few solutions:
process the input twice (really bad if you have a large number of entries); On the first pass, count the inputs. Then allocate the array, then read them again, into the allocated array.
read the inputs into a linked list; Each element in the list would hold a value, and the address of the next element.
Preallocate a block of memory and hope it is large enough to read the entire array. If it is not, reallocate a larger block and copy the already read values into it, then discard the initial block (this is what std::vector does).
This question already has answers here:
STATUS_ACCESS_VIOLATION when reading file int struct
(4 answers)
dynamically allocating memory to struct when reading from file in C++
(3 answers)
Closed 9 years ago.
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.
#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;
}
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);
I am having problems trying to serialise a vector (std::vector) into a binary format and then correctly deserialise it and be able to read the data. This is my first time using a binary format (I was using ASCII but that has become too hard to use now) so I am starting simple with just a vector of ints.
Whenever I read the data back the vector always has the right length but the data is either 0, undefined or random.
class Example
{
public:
std::vector<int> val;
};
WRITE:
Example example = Example();
example.val.push_back(10);
size_t size = sizeof BinaryExample + (sizeof(int) * example.val.size());
std::fstream file ("Levels/example.sld", std::ios::out | std::ios::binary);
if (file.is_open())
{
file.seekg(0);
file.write((char*)&example, size);
file.close();
}
READ:
BinaryExample example = BinaryExample();
std::ifstream::pos_type size;
std::ifstream file ("Levels/example.sld", std::ios::in | std::ios::binary | std::ios::ate);
if (file.is_open())
{
size = file.tellg();
file.seekg(0, std::ios::beg);
file.read((char*)&example, size);
file.close();
}
Does anyone know what I am doing wrong or what to do or be able to point me in the direction that I need to do?
You can't unserialise a non-POD class by overwriting an existing instance as you seem to be trying to do - you need to give the class a constructor that reads the data from the stream and constructs a new instance of the class with it.
In outline, given something like this:
class A {
A();
A( istream & is );
void serialise( ostream & os );
vector <int> v;
};
then serialise() would write the length of the vector followed by the vector contents. The constructor would read the vector length, resize the vector using the length, then read the vector contents:
void A :: serialise( ostream & os ) {
size_t vsize = v.size();
os.write((char*)&vsize, sizeof(vsize));
os.write((char*)&v[0], vsize * sizeof(int) );
}
A :: A( istream & is ) {
size_t vsize;
is.read((char*)&vsize, sizeof(vsize));
v.resize( vsize );
is.read((char*)&v[0], vsize * sizeof(int));
}
You're using the address of the vector. What you need/want is the address of the data being held by the vector. Writing, for example, would be something like:
size = example.size();
file.write((char *)&size, sizeof(size));
file.write((char *)&example[0], sizeof(example[0] * size));
I would write in network byte order to ensure file can be written&read on any platform. So:
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <arpa/inet.h>
int main(void) {
std::vector<int32_t> v = std::vector<int32_t>();
v.push_back(111);
v.push_back(222);
v.push_back(333);
{
std::ofstream ofs;
ofs.open("vecdmp.bin", std::ios::out | std::ios::binary);
uint32_t sz = htonl(v.size());
ofs.write((const char*)&sz, sizeof(uint32_t));
for (uint32_t i = 0, end_i = v.size(); i < end_i; ++i) {
int32_t val = htonl(v[i]);
ofs.write((const char*)&val, sizeof(int32_t));
}
ofs.close();
}
{
std::ifstream ifs;
ifs.open("vecdmp.bin", std::ios::in | std::ios::binary);
uint32_t sz = 0;
ifs.read((char*)&sz, sizeof(uint32_t));
sz = ntohl(sz);
for (uint32_t i = 0; i < sz; ++i) {
int32_t val = 0;
ifs.read((char*)&val, sizeof(int32_t));
val = ntohl(val);
std::cout << i << '=' << val << '\n';
}
}
return 0;
}
Read the other's answer to see how you should read/write a binary structure.
I add this one because I believe your motivations for using a binary format are mistaken. A binary format won't be easier that an ASCII one, usually it's the other way around.
You have many options to save/read data for long term use (ORM, databases, structured formats, configuration files, etc). The flat binary file is usually the worst and the harder to maintain except for very simple structures.