ifstream / ofstream issue with c++? - c++

I have been having a very hard time writing to a binary file and reading back. I am basically writing records of this format
1234|ABCD|efgh|IJKL|ABC
Before writing this record, I would write the length of this entire record ( using string.size()) and then I write the record to the binary file using ofstream as follows:
int size;
ofstream studentfile;
studentfile.open( filename.c_str(),ios::out|ios::binary );
studentfile.write((char*)&size,sizeof(int));
studentfile.write(data.c_str(),(data.size()*(sizeof(char))));
cout << "Added " << data << " to " << filename << endl;
studentfile.close();
And I read this data at some other place
ifstream ifile11;
int x;
std::string y;
ifile11.open("student.db", ios::in |ios::binary);
ifile11.read((char*)&x,sizeof(int));
ifile11.read((char*)&y,x);
cout << "X " << x << " Y " << y << endl;
first I read the length of the record into the variable x, and then read the record into string y. The problem is, the output shows x as being '0' and 'y' is empty.
I am not able figure this out. Someone who can look into this problem and provide some insight will be thanked very much.
Thank you

You can't read a string that way, as a std::string is really only a pointer and a size member. (Try doing std::string s; sizeof(s), the size will be constant no matter what you set the string to.)
Instead read it into a temporary buffer, and then convert that buffer into a string:
int length;
ifile11.read(reinterpret_cast<char*>(&length), sizeof(length));
char* temp_buffer = new char[length];
ifile11.read(temp_buffer, length);
std::string str(temp_buffer, length);
delete [] temp_buffer;

I know I am answering my own question, but I strictly feel this information is going to help everyone. For most part, Joachim's answer is correct and works. However, there are two main issues behind my problem :
1. The Dev-C++ compiler was having a hard time reading binary files.
2. Not passing strings properly while writing to the binary file, and also reading from the file. For the reading part, Joachim's answer fixed it all.
The Dev-C++ IDE didn't help me. It wrongly read data from the binary file, and it did it without me even making use of a temp_buffer. Visual C++ 2010 Express has correctly identified this error, and threw run-time exceptions and kept me from being misled.
As soon as I took all my code into a new VC++ project, it appropriately provided me with error messages, so that I could fix it all.
So, please do not use Dev-C++ unless you want to run into real troubles like thiis. Also, when trying to read strings, Joachim's answer would be the ideal way.

Related

C++ writing to file producing garbage data [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am trying to add data into a text file. However, i see for some reason it produces garbage data. I also notice, it will input the correct data once, but then it follow with garbage data.
void TextFileLogger::log(std::string msg){
using namespace std;
//ofstream output_file("students.data", ios::binary);
std::ofstream logFile;
// creating, opening and writing/appending data to a file
char filename[] = "log.txt";
logFile.open(filename, ios::binary | ios::app |ios::out);
if (logFile.fail())
{
std::cout << "The " << filename << " file could not be created/opened!" << std::endl;
// 0-normal, non zero - some errors
}
else
{
if (!logFile.write((char*)&msg, sizeof(msg)))
{
cout << "Could not write file" << endl;
}
else
{
streamsize bytesWritten = logFile.tellp();
if (bytesWritten != sizeof(msg))
{
cout << "Could not write expected number of bytes" << endl;
}
else
{
logFile << msg << std::endl;
cout << "file written OK" << endl;
}
}
}
}
That one is fun!
(char*)&msg does not do what you expect: std::string is mainly a pointer to a dynamically-allocated buffer which contains the actual data. When you take the std::string's address and try to read what's inside, you get a view of its innards, not its data. Using a C++ static_cast here would have spared you the trouble by telling you that the conversion makes no sense. sizeof(msg) similarly returns the size of the std::string, not the length of its data.
So, your solution is: use msg.data() and msg.size(), it's exactly what they're designed for.
But... why would it (sometimes) output your string, and a bunch of garbage? Well, std::strings typically use SSO (Small String Optimization). The std::string actually contains a small buffer, to store short enough strings without dynamic allocation. When you inspect the whole std::string object, you see this buffer pass by.
You are writing the contents of the whole std::string object, with all the member variables that it contains internally.
You either want:
logFile << msg;
or if you really want to use write():
logFile.write( msg.c_str(), msg.length());
And, I wonder: Why do create/open the file in binary mode, when you write strings afterwards?
And finally, you write the data twice, the second time in your last else clause.
The problem is with this line:
if (!logFile.write((char*)&msg, sizeof(msg)))
It should be this:
if (!logFile.write(msg.c_str(), msg.length()))
Since you are passing a std::string into the function, you should take advantage of the functions it provides (c_str() and length()) instead of trying to cast it to a char* (this always gets messy, plus you are casting away the const, which is also typically bad).
This:
if (!logFile.write((char*)&msg, sizeof(msg)))
is wrong in so many ways. msg is not an array of char, it's a std::string - lying to the compiler by using a cast is always a bad thing to do. And the size of a string is not the size of the characters it contains. Why the heck are you not using the obvios:
logfile << msg << std::endl;
Replace sizeof(msg) with msg.size(), sizeof() is not doing what you think!
Also (char*)&msg does not do whatever you think, use msg.data() instead.
logFile.write((char*)&msg, sizeof(msg));
should be rewritten to:
logFile.write(msg.data(), msg.size());
or, even better, because ofstream overrides operator<< for std::string:
logfile << msg;

Save the variables of an object to then be able to initialise another object with those variables

What I am trying to achieve is this:
Let's say I have a class Score. This class has an int variable and a char* variable.
Now when I have an object Score score, I would like to be able to save the value of those variables (I guess to a file). So now this file has an int variable and a char* variable that I can then access later to create a new Score object.
So I create Score score(10, "Bert");. I either do something like score.SaveScore(); or the score gets saved when the game is over or the program exits, it doesn't matter.
Basically I am looking for the equivalent/correct way of doing this:
score.SaveScore(FILE file)
{
file.var1 = score.score;
file.var2 = score.name;
}
I realize this is probably very stupid and not done this way whatsoever! This is just me trying to explain what I am trying to achieve in the simplest way possible.
Anyway, when I run the program again, that original Score score(10, "Bert") does not exist any more. But I would like to be able to access the saved score(from file or wherever it may be) and create another Score object.
So it may look something like:
LoadScore(FILE file)
{
Score newScore(file.var1, file.var2);
}
Again, just trying to show what I am trying to achieve.
The reason why I want to be able to access the variables again is to eventually have a Scoreboard, the Scoreboard would load a bunch of scores from the file.
Then when a new score is created, it is added to the scoreboard, compared to the other scores currently in the scoreboard and inserted in the right position (like a score of 6 would go in between 9 and 4).
I feel like this was a bit long winded but I was trying to really explain myself well! Which I hope I did!
Anyway, I am not looking for someone to tell me how to do all of that.
All I am after is how to do the initial save to a file.
Thank you for any suggestions.
I would use the <fstream> library, like this;
//example values
int x=10;
float y=10.5;
const char* chars = "some random value";
string str(chars); //make string buffer for sizing
str.resize(20); //make sure its fixed size
//open a test.txt file, in the same dir for output
std::ofstream os("test.txt", std::ios::out | std::ios::binary); //make it output binary
//(char*) cast &x, sizeof(type) for values/write to file chars for x and y
os.write((char*)&x, sizeof(int)); //only sizeof(int) starting at &x
os.write((char*)&y, sizeof(float)); //cast as a char pointer
os.write(str.data(), sizeof(char)*str.size()); //write str data
os.close();
//the file test.txt will now have binary data in it
//to read it back in, just ifstream, and put that info in new containers, like this;
int in_x = 0; //new containters set to 0 for debug
float in_y = 0;
char inchar[20]; //buffer to write 20 chars to
ifstream is("test.txt", std::ios::in | std::ios::binary); //read in binary
is.read((char*)&in_x, sizeof(int)); //write to new containers
is.read((char*)&in_y, sizeof(float));
is.read((char*)&inchar, sizeof(char)*20); //write char assuming 20 size
is.close();
//outputting will show the values are correctly read into the new containers
cout << in_x << endl;
cout << in_y << endl;
cout << inchar << endl;
I realize this is probably very stupid and not done this way whatsoever!
The entire software industry was stupid enough to have it done so many times that even a special term was invented for this operation - serialization and nearly all C++ frameworks and libraries have implemented this in a various ways.
Since question is tagged with C++ I would suggest you to look at boost serialization but there are many other implementations.
Do you need that file to be readable by a human? If yes than consider, for example, XML or JSON formats.
You don't need it be readable but want it be as compact as possible? Consider google protobuf
Just start doing it and come with a more specific question(s).
As it was mentioned before, keep strings as std:string objects rather then char*
About writing/reading to/from files in C++ read about fstream

Reading a text file into a char array, then into a char linked list

So currently I am working on an assignment, and for a section of it I need to be able to read a .txt file into a linked list of type char. I was already confused trying to do this, so I set out on a different path and decided to try to copy the text from the file into a char array, and then one by one copy the values in the array into the linked list.
So far I have my program compiling and running up to a certain point, before I receive the error Segmentation fault (core dumped).
The code for reading the file is as follow:
void readFile(list<char> &originList, string fileName){
ifstream fileInput;
fileInput.open(fileName.c_str());
int arraySize = fileInput.gcount();
char tempHold[arraySize];
if (!fileInput) {
cout << "Can't open file: " << fileName << "\n";
} else {
string contents((istreambuf_iterator<char>(fileInput)), istreambuf_iterator<char>());
strcpy (tempHold, contents.c_str());
for (int x = 0; x < fileInput.gcount(); x++) {
originList.push_back(tempHold[x]);
}
}
fileInput.close();
}
Also to add some context, using cout I determined that the code stops running, instead presenting the error, at the following point:
strcpy (tempHold, contents.data());
Also, I am not 100% on how exactly they work, only a loose idea to be honest. I mostly sourced the idea from this Stack Overflow question,
How to copy a .txt file to a char array in c++, but got confused somewhere a long the way.
Thanks in advance for any help you can provide.
istream::gcount returns the number of characters extracted by the last unformatted input operation performed on the object. Since you did not read anything from the file, you should get something wrong.Call, for example, istream.getline(); before calling gcount()
Like my comment to your question says, read each character from the file and add it to the list.
void readFile(list<char> &originList, string fileName) {
ifstream fileInput(fileName.c_str());
if (!fileInput) {
cout << "Can't open file: " << fileName << "\n";
}
char c;
while (fileInput.get(c)) {
originList.push_back(c);
}
}
Note: while (fileInput.get(c)) This reads the character and returns the stream. When a stream is used as a bool value it checks to see if the stream is valid. Valid means eof() and bad() are both false. - From a comment to the answer to the question linked below.
My answer was adapted from this other Stack Overflow question: Reading from text file until EOF repeats last line

Parse int to string with stringstream

Well!
I feel really stupid for this question, and I wholly don't mind if I get downvoted for this, but I guess I wouldn't be posting this if I had not at least made an earnest attempt at looking for the solution.
I'm currently working on Euler Problem 4, finding the largest palindromic number of two three-digit numbers [100..999].
As you might guess, I'm at the part where I have to work with the integer I made. I looked up a few sites and saw a few standards for converting an Int to a String, one of which included stringstream.
So my code looked like this:
// tempTotal is my int value I want converted.
void toString( int tempTotal, string &str )
{
ostringstream ss; // C++ Standard compliant method.
ss << tempTotal;
str = ss.str(); // Overwrite referenced value of given string.
}
and the function calling it was:
else
{
toString( tempTotal, store );
cout << loop1 << " x " << loop2 << "= " << store << endl;
}
So far, so good. I can't really see an error in what I've written, but the output gives me the address to something. It stays constant, so I don't really know what the program is doing there.
Secondly, I tried .ToString(), string.valueOf( tempTotal ), (string)tempTotal, or simply store = temptotal.
All refused to work. When I simply tried doing an implicit cast with store = tempTotal, it didn't give me a value at all. When I tried checking output it literally printed nothing. I don't know if anything was copied into my string that simply isn't a printable character, or if the compiler just ignored it. I really don't know.
So even though I feel this is a really, really lame question, I just have to ask:
How do I convert that stupid integer to a string with the stringstream? The other tries are more or less irrelevant for me, I just really want to know why my stringstream solution isn't working.
EDIT:
Wow. Seriously. This is kind of embarrassing. I forgot to set my tempTotal variable to something. It was uninitialized, so therefore I couldn't copy anything and the reason the program gave me either a 0 or nothing at all.
Hope people can have a laugh though, so I think this question would now be better suited for deletion since it doesn't really serve a purpose unless xD But thanks to everybody who tried to help me!
Have you tried just outputting the integer as is? If you're only converting it to a string to output it, then don't bother since cout will do that for you.
else
{
// toString( tempTotal, store ); // Skip this step.
cout << loop1 << " x " << loop2 << "= " << tempTotal << endl;
}
I have a feeling that it's likely that tempTotal doesn't have the value you think it has.
I know this doesn't directly answer your question but you don't need to write your own conversion function, you can use boost
#include <boost/lexical_cast.hpp>
using boost::lexical_cast;
//usage example
std::string s = lexical_cast<std::string>(tempTotal);
Try the following:
string toString(int tempTotal)
{
ostringstream ss;
ss << tempTotal;
return ss.str();
}
string store = toString(tempTotal);
If you want to output the integer, you don't even need to convert it; just insert it into the standard output:
int i = 100;
cout << i;
If you want the string representation, you're doing good. Insert it into a stringstream as you did, and ask for it's str().
If that doesn't work, I suggest you minimize the amount of code, and try to pinpoint the actual problem using a debugger :)
Short answer: your method to convert an int to a string works. Got any other questions?

How do you output variable's declared as a double to a text file in C++

I am very new to C++ and I am wondering how you output/write variables declared as double to a txt file. I know about how to output strings using fstream but I cant figure out how to send anything else. I am starting to think that you can't send anything but strings to a text file is that correct? If so then how would you convert the information stored in the variable to a string variable?
Here is my code that I'm trying to implement this concept into, Its fairly simple:
int main()
{
double invoiceAmt = 3800.00;
double apr = 18.5; //percentage
//compute cash discount
double discountRate = 3.0; //percentage
double discountAmt;
discountAmt = invoiceAmt * discountRate/100;
//compute amount due in 10 days
double amtDueIn10;
amtDueIn10 = invoiceAmt - discountAmt;
//Compute Interest on the loan of amount (with discount)for 20 days
double LoanInt;
LoanInt = amtDueIn10 * (apr /360/100) * 20;
//Compute amount due in 20 days at 18.5%.
double amtDueIn20;
amtDueIn20 = invoiceAmt * (1 + (apr /360/100) * 20);
return 0;
}
So what I'm trying to do is use those variables and output them to the text file. Also please inform me on the includes that I need to use for this source code. Feel free to give suggestions on how to improve my code in other ways as well please.
Thanks in advance.
As your tagging suggests, you use file streams:
std::ofstream ofs("/path/to/file.txt");
ofs << amtDueIn20;
Depending on what you need the file for, you'll probably have to write more stuff (like whitespaces etc.) in order to get decent formatting.
Edit due to rmagoteaux22's ongoing problems:
This code
#include <iostream>
#include <fstream>
const double d = 3.1415926;
int main(){
std::ofstream ofs("test.txt");
if( !ofs.good() ) {
std::cerr << "Couldn't open text file!\n";
return 1;
}
ofs << d << '\n';
return 0;
}
compiles for me (VC9) and writes this to test.txt:
3.14159
Can you try this?
Simply use the stream write operator operator<< which has an overloaded definition for double (defined in basic_ostream)
#include <fstream>
...
std::fstream stmMyStream( "c:\\tmp\\teststm.txt", std::ios::in | std::ios::out | std::ios::trunc );
double dbMyDouble = 23.456;
stmMyStream << "The value is: " << dbMyDouble;
To answer your first question, in C you use printf (and for file output fprintf). IIRC, cout has a large number of modifiers also, but I won't mention them as you originally mentioned fstream (more 'C' centric than C++) --
oops, missed the ofstream indicator, ignore my 'C' comments and use C++
to improve your program, be sure to use parentheses a lot when doing computations as above to be 100% sure things are evaluated the way you want them to be (do not rely on order of precedence)
Generally speaking methods to write to a output are printf, wprintf etc.
In case of files, these methods are named as fprintf_s, fsprintf_s etc.
Note that the '_s' methods are the new secure variations of previous formatting methods. You should always use these new secure versions.
For examples refer to:
http://msdn.microsoft.com/en-us/library/ksf1fzyy%28VS.80%29.aspx
Note these methods use a format specifier to convert a given type to text. For example %d acts as a place holder for integer. Similarly %f for double.
Just use the << operator on an output stream:
#include <fstream>
int main() {
double myNumber = 42.5;
std::fstream outfile("test.txt", std::fstream::out);
outfile << "The answer is almost " << myNumber << std::endl;
outfile.close();
}
I was having the exact same problem, where ofstream was outputting strings, but stopped as soon as it reached a variable. With a bit more Googling I found this solution in a forum post:
Under Xcode 3.2 when creating a new project based on stdc++ project template the target build settings for Debug configuration adds preprocessor macros which are incompatible with gcc-4.2:
_GLIBCXX_DEBUG=1
_GLIBXX_DEBUG_PEDANTIC=1
Destroy them if you want Debug/gcc-4.2 to execute correctly.
http://forums.macrumors.com/showpost.php?p=8590820&postcount=8