C++ reading/writing objects with binary files - c++

I have gone through hours of time trying to fix the issue of binary file manipulation.
The task is to read and write BookStoreBook objects to/from a binary file
The BookStoreBook class contains the following member variables:
string isbn;
string title;
Author author;
string publisher;
Date dateAdded;
int quantityOnHand;
double wholesaleCost;
double retailPrice;
The code for reading books is as shown:
fstream file("inventory.txt", ios::binary | ios::in | ios::out);
vector<BookStoreBook> books:
BookStoreBook *book = (BookStoreBook *)new char[sizeof(BookStoreBook)];
file.read((char*)book, sizeof(BookStoreBook));
while (!file.eof())
{
books.push_back(*book);
file.read((char*)book, sizeof(BookStoreBook));
}
The code for writing books is as shown:
vector<BookStoreBook> writeBooks = library.getBooks(); //library contains books
file.close();
file.open("inventory.txt", ios::out | ios::binary);
for(int i = 0; i < writeBooks.size(); i++)
{
BookStoreBook *book = (BookStoreBook *)new char[sizeof(BookStoreBook)];
book = &writeBooks[i];
file.write((char*)book, sizeof(BookStoreBook));
file.clear();
}
file.clear();
file.close();
I don't want to convert any string to a c_str(), as this is prohibited in the assignment requirements.
Some notes:
Right when I run the program, the program tries to read books from the file, and
that is when we get a Windows error window, later when i debug, i get the following message:
Unhandled exception at 0x56b3caa4 (msvcr100d.dll) in FinalProject.exe: 0xC0000005: Access violation reading location 0x0084ef10
The funny thing is, sometimes the program runs perfectly fine, and
sometimes it crashes when it first reads the books from the file.
However, whenever the program has successfully read some contents, and
I dont modify the books, and then reopen the program, the program keeps
running perfectly.
Nothing seems to work. Please help!

Your problem here is that certain parts of your BookStoreBook class contain pointers, even though they are not visible. std::string for example has a pointer to the memory location where the contents of the string are kept.
It is virtually always considered a bad practice to write data structures in C++ to disk as they appear in memory. Doing this does not account for different endianness of different machines, word width on different machines (int or long may differ in size on 32bit and 64bit machines), and you run into all the pointer trouble.
You should push each of the fields of your BookStoreBook to the output stream, along the lines of
file << book.isbn << ' ';
file << book.title << ' ';
...
Note that the above is very bad practice, as the decoding gets horribly difficult. I suggest you use Boost.Serialization for this, or write your own methods that can read/write key-value-pairs from a file, or you might want to look into jsoncpp or tinyxml2. This whole topic can get quite convoluted, so sticking with Boost is a good idea, even if just to figure out how to solve the issue yourself (assuming this is a homework assignment).

Related

I need help using RapidJSON

I'm trying to parse a JSON file using RapidJSON that has thousands of objects like this one
"Amateur Auteur": {
"layout": "normal",
"name": "Amateur Auteur",
"manaCost": "{1}{W}",
"cmc": 2,
"colors": [
"White"
],
"type": "Creature — Human",
"types": [
"Creature"
],
"subtypes": [
"Human"
],
"text": "Sacrifice Amateur Auteur: Destroy target enchantment.",
"power": "2",
"toughness": "2",
"imageName": "amateur auteur",
"colorIdentity": [
"W"
]
},
I believe I have stored the JSON as a C string correctly I just can't really understand how to use the RapidJSON library to return the values I want from each object.
This is the code for storing the JSON as a C string and then parsing it in case I am doing something incorrect here.
std::ifstream input_file_stream;
input_file_stream.open("AllCards.json", std::ios::binary | std::ios::ate); //Open file in binary mode and seek to end of stream
if (input_file_stream.is_open())
{
std::streampos file_size = input_file_stream.tellg(); //Get position of stream (We do this to get file size since we are at end)
input_file_stream.seekg(0); //Seek back to beginning of stream to start reading
char * bytes = new char[file_size]; //Allocate array to store data in
if (bytes == nullptr)
{
std::cout << "Failed to allocate the char byte block of size: " << file_size << std::endl;
}
input_file_stream.read(bytes, file_size); //read the bytes
document.Parse(bytes);
input_file_stream.close(); //close file since we are done reading bytes
delete[] bytes; //Clean up where we allocated bytes to prevent memory leak
}
else
{
std::cout << "Unable to open file for reading.";
}
Your post seems to ask multiple questions. Lets start from the beginning.
I believe I have stored the JSON as a C string correctly I just can't
really understand how to use the RapidJSON library to return the
values I want from each object.
This is a big no no in software engineering. Never believe or assume. It will come back on release day and haunt you. Instead validate your assertion. Here are a few steps starting from the easy to the more involved.
Print out your C-String.
The easiest way to confirm the content of your variable, especially string data, is to simply print to the screen. Nothing easier then seeing your JSON data print to the screen to confirm you have read it in correctly.
std::cout.write(bytes, filesize);
Breakpoint / Debugger
If you have some reason for not printing out your variable, then compile your code with debugging enabled and load in GDB if you're using g++, lldb if you're using clang++, or simply place a breakpoint in visual studio if you're using an VS or VSCode. Once at the breakpoint you can inspect the content of your variable.
However, before we move on I wouldn't be helping you if I didn't point out that reading files in CPP is much much easier then the way you're reading.
// Open File
std::ifstream in("AllCards.json", std::ios::binary);
if(!in)
throw std::runtime_error("Failed to open file.");
// dont skip on whitespace
std::noskipws(in);
// Read in content
std::istreambuf_iterator<char> head(in);
std::istreambuf_iterator<char> tail;
std::string data(head, tail);
At the end of the above code you now have all of the content from your file read into an std::string which wraps a null terminated or C-String. You can access that data by calling .c_str() on your string instances. If you do it this way you no longer have to worry about calling new or delete[] as the std::string class takes care of the buffer for you. Just make sure it hangs around as long as you're using it in RapidJSON.
This is the code for storing the JSON as a C string and then parsing
it in case I am doing something incorrect here.
No. according the the rapid JSON documentation you create a document object and have it parse the string.
Document d;
d.Parse(data.c_str());
However, that just creates the element for querying the document. You can ask the document if specific items exist (d.hasMember("")), ask for a string typed members content d["name"].GetString() or anything listed over at the documentation. You can read the tutorial here.
By the way. Welcome to SO. I would suggest that next time you post ask a more targeted question. What exactly are you trying to do with the parsed JSON element?
I just can't really understand how to use the RapidJSON library to
return the values I want from each object.
I cannot answer this question for two reasons. What are you trying to extract? What have you tried? Have you read the documentation and do not understand a specific item?
Here is a good place to read up on asking better questions. Please don't think I am coming down on you. I bring this up because asking better questions will get you better and more specific answers. poorly asked questions always run the risk of being ignored or, dare I say it, the good old does google not work today response.
https://stackoverflow.com/help/how-to-ask
** Updates **
To your question. You can iterate over all objects.
using namespace rapidjson;
Document d;
d.Parse(data.c_str());
for (auto itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr){
std::cout << itr->name.GetString() << '\n';
}

Writing to a .csv file with C++?

TL;DR I am trying to take a stream of data and make it write to a .csv file. Everything is worked out except the writing part, which I think is simply due to me not referencing the .csv file correctly. But I'm a newbie to this stuff, and can't figure out how to correctly reference it, so I need help.
Hello, and a big thank you in advance to anyone that can help me out with this! Some advance info, my IDE is Xcode, using C++, and I'm using the Myo armband from Thalmic Labs as a device to collect data. There is a program (link for those interested enough to look at it) that is supposed to stream the EMG, accelerometer, gyroscope, and orientation values into a .csv file. I am so close to getting the app to work, but my lack of programming experience has finally caught up to me, and I am stuck on something rather simple. I know that the app can stream the data, as I have been able to make it print the EMG values in the debugging area. I can also get the app to open a .csv file, using this code:
const char *path= "/Users/username/folder/filename";
std::ofstream file(path);
std::string data("data to write to file");
file << data;
But no data ends up being streamed/printed into that file after I end the program. The only thing that I can think might be causing this is that the print function is not correctly referencing this file pathway. I would assume that to be a straightforward thing, but like I said, I am inexperienced, and do not know exactly how to address this. I am not sure what other information is necessary, so I'll just provide everything that I imagine might be helpful.
This is the function structure that is supposed to open the files: (Note: The app is intended to open the file in the same directory as itself)
void openFiles() {
time_t timestamp = std::time(0);
// Open file for EMG log
if (emgFile.is_open())
{
emgFile.close();
}
std::ostringstream emgFileString;
emgFileString << "emg-" << timestamp << ".csv";
emgFile.open(emgFileString.str(), std::ios::out);
emgFile << "timestamp,emg1,emg2,emg3,emg4,emg5,emg6,emg7,emg8" << std::endl;
This is the helper to print accelerometer and gyroscope data (There doesn't appear to be anything like this to print EMG data, but I know it does, so... Watevs):
void printVector(std::ofstream &path, uint64_t timestamp, const myo::Vector3< float > &vector)
{
path << timestamp
<< ',' << vector.x()
<< ',' << vector.y()
<< ',' << vector.z()
<< std::endl;
}
And this is the function structure that utilizes the helper:
void onAccelerometerData(myo::Myo *myo, uint64_t timestamp, const myo::Vector3< float > &accel)
{
printVector(accelerometerFile, timestamp, accel);
}
I spoke with a staff member at Thalmic Labs (the guy who made the app actually) and he said it sounded like, unless the app was just totally broken, I was potentially just having problems with the permissions on my computer. There are multiple users on this computer, so that may very well be the case, though I certainly hope not, and I'd still like to try and figure it out one more time before throwing in the towel. Again, thanks to anyone who can be of assistance! :)
My imagination is failing me. Have you tried writing to or reading from ostringstream or istringstream objects? That might be informative. Here's a line that's correct:
std::ofstream outputFile( strOutputFilename.c_str(), std::ios::app );
Note that C++ doesn't have any native support for streaming .csv code, though, you may have to do those conversions yourself. :( Things may work better if you replace the "/"'s by (doubled) "//" 's ...

Brought a Linux C++ Console Application to a Win32 C++ App using VS2010 and the search function from <algorithm> is no longer working

Just like the title says, I've been working on a fairly large program and have come upon this bug. I'm also open to alternatives for searching a file for a string instead of using . Here is my code narrowed down:
istreambuf_iterator<char> eof;
ifstream fin;
fin.clear();
fin.open(filename.c_str());
if(fin.good()){
//I outputted text to a file to make sure opening the file worked, which it does
}
//term was not found.
if(eof == search(istreambuf_iterator<char>(fin), eof, term.begin(), term.end()){
//PROBLEM: this code always executes even when the string term is in the file.
}
So just to clarify, my program worked correctly in Linux but now that I have it in a win32 app project in vs2010, the application builds just fine but the search function isn't working like it normally did. (What I mean by normal is that the code in the if statement didn't execute because, where as now it always executes.)
NOTE: The file is a .xml file and the string term is simply "administration."
One thing that might or might not be important is to know that filename (filename from the code above) is a XML file I have created in the program myself using the code below. Pretty much I create an identical xml file form the pre-existing one except for it is all lower case and in a new location.
void toLowerFile(string filename, string newloc, string& newfilename){
//variables
ifstream fin;
ofstream fout;
string temp = "/";
newfilename = newloc + temp + newfilename;
//open file to read
fin.open(filename.c_str());
//open file to write
fout.open(newfilename.c_str());
//loop through and read line, lower case, and write
while (fin.good()){
getline (fin,temp);
//write lower case version
toLowerString(temp);
fout << temp << endl;
}
//close files
fout.close();
fin.close();
}
void toLowerString(string& data){
std::transform(data.begin(), data.end(), data.begin(), ::tolower);
}
I'm afraid your code is invalid - the search algorithm requires forward iterators, but istreambuf_iterator is only an input iterator.
Conceptually that makes sense - the algorithm needs to backtrack on a partial match, but the stream may not support backtracking.
The actual behaviour is undefined - so the implementation is allowed to be helpful and make it seem to work, but doesn't have to.
I think you either need to copy the input, or use a smarter search algorithm (single-pass is possible) or a smarter iterator.
(In an ideal world at least one of the compilers would have warned you about this.)
Generally, with Microsoft's compiler, if your program compiles and links a main() function rather than a wmain() function, everything defaults to char. It would be wchar_t or WCHAR if you have a wmain(). If you have tmain() instead, then you are at the mercy of your compiler/make settings and it's the UNICODE macro that determines which flavor your program uses. But I doubt that char_t/wchar_t mismatch is actually the issue here because I think you would have got an warning or error if all four of the search parameters didn't use the same the same character width.
This is a bit of a guess, but try this:
if(eof == search(istreambuf_iterator<char>(fin.rdbuf()), eof, term.begin(), term.end())

Taking the contents of a vector and placing them in a text file

I'm working on an assignment for my computer science class, its a first year course as I'm a beginner and I am having trouble with a certain part.
A quick explanation of what my assignment does is:
It takes information from a text file and puts it in a vector while the program is running, and you can add names to it or remove names from it, and once you are done you need it to save the information, which means you have to take the information back out of the vector and replace it into the text file.
I haven't learned of a way to take information out of a vector and back into a text-file, I saw that a classmate of mine posted on here a few times but he was pretty much dismissed so he told me to ask the question for myself.
We were given a bit of the coding for our program and honestly I have got no clue on how to make the function take the information back out of the vector and into the text file updated.
What ive included:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
here is the function in which it would save into, any help would be greatly appreciated.
void quit_program(string strFileName, vector<string>&vecThisStudent)
{
//write the body of the function
//Hint: this is the function you should write the vector’s content back to the file.
cout<<"Thanks for using the program. Program terminated"<<endl;
}
As you can see we were even given the hint on what the function was supposed to do, but anyone I have spoken to from the class hasnt had a clue on how to get it done (or they dont like me enough to tell me)
If the entire program is needed, I can post it. It looks almost identical to my classmate who posted earlier, but that is just because we were given the majority of the code and we just had to complete a few different things, and I've just been stuck here for the last 10 hours or so.
My read_file
int read_file(string strFileName, vector<string>&vecThisStudent, fstream &inFile)
{
string strFirstName
string strLastName;
inFile.open(strFileName.c_str(), ios::in | ios::out | ios::app);
while (inFile >> strFirstName >> strLastName)
{
vecThisStudent.push_back(strFirstName + " " + strLastName);
}
return 0;
}
Split the problem into sub-problems. Keep splitting to a smaller pieces till each piece is manageable.
In your case sub-problems I would be comfortable working with are "C++ performing action at program exit", "C++ container serialize", "C++ file IO".
The first one will give you C: Doing something when the program exits, the second - ostream iterator usage in c++, and finally the third one - Writing in file C++.
As a final step you just need to combine all three back together.
And Steve, do not blame your professor or your destiny. Being a good programmer is as hard as being a good surgeon, as hard and as rewarding, but requires quite a bit of dedication to grow from mediocrity to a sharp Swiss Army Knife. At your first job interview you'll see how much worse questions can be than ones asked in these assignments.
Seeing your lack of C++ knowledge, I would REALLY suggest watching some tutorials about C++. If you don't know what a for-loop is/how to use it, you will have MAJOR problems with future assignments.
Here are some great series of tutorial.
There's no such thing are taking the contents of a file (or vector) and placing it automatically into a vector (or file).
But to read or write data, take a look at this page.
The general idea of reading a file is:
Iterate though the file and read each input one by one.
Place that input into a vector
The general idea of outputting data to a file is:
Iterate though the data (ex: every element of that vector)
Output that data (ex: that element).
By iterating, I mean running though the data (usually by a for-loop):
int write_file(string strFileName, vector<string>&vecThisStudent, fstream &outFile)
{
outFile.open(strFileName.c_str(), ios::in | ios::out | ios::app);
for (int i = 0 ; i < vecThisStudent.size() ; i++) {
//Use this line to output to console
cout << vecThisStudent[i] << " \n";
//Use this line to output to file
outFile << vecThisStudent[i] << "\n";
}
}
Use ofstream
http://www.cplusplus.com/reference/fstream/ofstream/
Open File..
Write data using << (http://www.cplusplus.com/reference/ostream/ostream/operator%3C%3C/)
Close file..
I am not sure what exactly you stuck with..

Ifstream fails for unknown reason

I have a problem with the function used to read the pgm file format to the memory .
I used the sources in the following link http://www.cse.unr.edu/~bebis/CS308/Code/ReadImage.cpp . You can find others in the same directory ; and some instructions in CS308 ; if you’re interested .
The problem is ifstream ifp fails ; and I think this piece of code maybe the reason ; but it looks fine with me .
Any ideas will be appreciated
charImage = (unsigned char *) new unsigned char [M*N];
ifp.read( reinterpret_cast<char *>(charImage), (M*N)*sizeof(unsigned char));
if (ifp.fail()) {
cout << "Image " << fname << " has wrong size" << endl;
exit(1);
}
The problem is that your input file is not formatted properly. It should have enough data to fill charImage, but it doesn't, and this is why it's failing. Another possibility is that you are trying to run this code on windows, and need to open the file in binary mode.
Specifically (for the binary part) change:
ifp.open(fname, ios::in);
to:
ifp.open(fname, ios::in | ios::binary);
As an aside, it is generally inappropriate to cast the result of a new operator. Here, it's just redundant and doesn't make any sense.
Anything using reinterpret_cast<...>() looks suspicious to me, to say the least. It is probably not the root of the problem, though. My personal guess is that the root of the problem is running the code on a Windows machine and not opening the file in binary mode. Try using
std::ifstream in("filename", std::ios_base:::binary);
Since the code opening the file isn't part of the question it is just a wild guess, though.