C++ binary files I/O, data lost when writing - c++

I am learning C++ with the "Programming: Principles and Practice Using C++" book from Bjarne Stroustrup. I am currently studying chapter 11 and I found an example on how to read and write binary files of integers (section 11.3.2). I played around with the example and used a .txt file (input.txt) with a sentence which I read and wrote to another file (output.txt) (text_to_binary fnc) and then read and wrote back to the original file (input.txt) (binary_to_text fnc).
#include<fstream>
#include<iostream>
using namespace std;
void text_to_binary(ifstream &ifs, ofstream &ofs)
{
for (int x; ifs.read(as_bytes(x), sizeof(char));)
{
ofs << x << '\n';
}
ofs.close();
ifs.close();
}
void binary_to_text(ifstream &ifs, ofstream &ofs)
{
for (int x; ifs >> x;)
{
ofs.write(as_bytes(x), sizeof(char));
}
ifs.close();
ofs.close();
}
int main()
{
string iname = "./chapter_11/input.txt";
string oname = "./chapter_11/output.txt";
ifstream ifs{iname, ios_base::binary};
ofstream ofs{oname, ios_base::binary};
text_to_binary(ifs, ofs);
ifstream ifs2{oname, ios_base::binary};
ofstream ofs2{iname, ios_base::binary};
binary_to_text(ifs2, ofs2);
return 0;
}
I figured out that I have to use sizeof(char) rather than sizeof(int) in the .read and .write command. If I use the sizeof(int) some chars of the .txt file go missing when I write them back to text. Funnily enough chars only goes missing if
x%4 != 0 (x = nb of chars in .txt file)
example with sizeof(int):
input.txt:
hello this is an amazing test. 1234 is a number everything else doesn't matter..asd
(text_to_binary fnc) results in:
output.txt:
1819043176
1752440943
1763734377
1851859059
1634558240
1735289210
1936028704
824192628
540291890
1629516649
1836412448
544367970
1919252069
1768453241
1696622446
543519596
1936027492
544483182
1953784173
774795877
(binary_to_text fnc) results back in:
input.txt:
hello this is an amazing test. 1234 is a number everything else doesn't matter..
asd went missing.
Now to my question, why does this happen? Is it because int's are saved as 4 bytes?
Bonus question: Out of interest, is there a simpler/more efficient way of doing this?
edit: updated the question with the results to make it hopefully more clear

When you attempt to do a partial read, the read will attempt to go beyond the end of the file and the eof flag will be set for the stream. This makes its use in the loop condition false so the loop ends.
You need to check gcount of the stream after the loop to see if any bytes was actually read into the variable x.
But note that partial reads will only write to parts of the variable x, leaving the rest indeterminate. Exactly which parts depends on the system endianness, and using the variable with its indeterminate bits will lead to undefined behavior.

Related

Filling a cstring using <cstring> with text from a textfile using File I/O C++

I began learning strings yesterday and wanted to manipulate it around by filling it with a text from a text file. However, upon filling it the cstring array only prints out the last word of the text file. I am a complete beginner, so I hope you can keep this beginner friendly. The lines I want to print from the file are:
"Hello World from UAE" - First line
"I like to program" - Second line
Now I did look around and eventually found a way and that is to use std::skipary or something like that but that did not print it the way I had envisioned, it prints letter by letter and skips each line in doing so.
here is my code:
#include <fstream>
#include <iostream>
#include <cstring>
#include <cctype>
using namespace std;
int main() {
ifstream myfile;
myfile.open("output.txt");
int vowels = 0, spaces = 0, upper = 0, lower = 0;
//check for error
if (myfile.fail()) {
cout << "Error opening file: ";
exit(1);
}
char statement[100];
while (!myfile.eof()) {
myfile >> statement;
}
for (int i = 0; i < 30; ++i) {
cout << statement << " ";
}
I'm not exactly sure what you try to do with output.txt's contents, but a clean way to read through a file's contents using C++ Strings goes like this:
if (std::ifstream in("output.txt"); in.good()) {
for (std::string line; std::getline(in, line); ) {
// do something with line
std::cout << line << '\n';
}
}
You wouldn't want to use char[] for that, in fact raw char arrays are hardly ever useful in modern C++.
Also - As you can see, it's much more concise to check if the stream is good than checking for std::ifstream::fail() and std::ifstream::eof(). Be optimistic! :)
Whenever you encounter output issues - either wrong or no output, the best practise is to add print (cout) statements wherever data change is occurring.
So I first modified your code as follows:
while (!myfile.eof()) {
myfile >> statement;
std::cout<<statement;
}
This way, the output I got was - all lines are printed but the last line gets printed twice.
So,
We understood that data is being read correctly and stored in statement.
This raises 2 questions. One is your question, other is why last line is printed twice.
To answer your question exactly, in every loop iteration, you're reading the text completely into statement. You're overwriting existing value. So whatever value you read last is only stored.
Once you fix that, you might come across the second question. It's very common and I myself came across that issue long back. So I'm gonna answer that as well.
Let's say your file has 3 lines:
line1
line2
line3
Initially your file control (pointer) is at the beginning, exactly where line 1 starts. After iterations when it comes to line3, we know it's last line as we input the data. But the loop control doesn't know that. For all it knows, there could be a million more lines. Only after it enters the loop condition THE NEXT TIME will it come to know that the file has ended. So the final value will be printed twice.

C++: FStream thinks it has reached EOF of binary file

I'm trying to read a binary file with the following format:
-64 bit integer
-3276 32-bit floats
-(Repeat last 2 lines until eof)
This is the block where I interpret the file:
ifstream bbrFile;
ofstream csvFile;
bbrFile.open(inFilename);
csvFile.open(dataFilename);
//yes I did actually check to make sure that the files had opened.
//I omitted it here for brevity
long long int time;
float point;
while (bbrFile)
{
bbrFile.read((char*)&time, sizeof(time));
csvFile << time;
for (int i = 0; i < 3276; i++) {
bbrFile.read((char*)&point, sizeof(point));
csvFile << ',' << point;
}
csvFile << "\n";
}
So far, my code is working fine, except that it thinks it's reached the end of file after reading in about 53 floats, and then just outputs the last float it read until the 'for' loop ends. I've tried using fread and FILE* instead of read and fstream, and gotten identical results. I've also tried replacing
while (bbrFile)
with
while (!bbrFile.eof())
To no avail.
Since the binary file is about 12 megabytes, I'm somewhat as a loss as to why it stops reading here.
To read the file as a binary file, you should add binary to the file mode:
bbrFile.open(inFilename, ios::binary);
otherwise it will be read as a text file, and some codes could be interpreted as an end-of-file mark.

Converting between text files and binary files in C++

For converting an ordinary text file into binary and then convert that binary file back to a text file so that the first text file equals with the last text file, I have wrote below code.
But the bintex text file and the final text file aren't equal. I don't know which part of code is incorrect.
Input sample ("bintex") contains this: 1983 1362
The result ("final") contains this: 959788084
which of course are not equal.
#include <iostream>
#include <fstream>
using namespace std;
int main() try
{
string name1 = "bintex", name2 = "texbin", name3 = "final";
ifstream ifs1(name1.c_str());
if(!ifs1) error("Can't open file for reading.");
vector<int>v1, v2;
int i;
while(ifs1.read(as_bytes(i), sizeof(int)));
v1.push_back(i);
ifs1.close();
ofstream ofs1(name2.c_str(), ios::binary);
if(!ofs1) error("Can't open file for writting.");
for(int i=0; i<v1.size(); i++)
ofs1 << v1[i];
ofs1.close();
ifstream ifs2(name2.c_str(), ios::binary);
if(!ifs2) error("Can't open file for reading.");
while(ifs2.read(as_bytes(i), sizeof(int)));
v2.push_back(i);
ifs2.close();
ofstream ofs2(name3.c_str());
if(!ofs2) error("Can't open file for writting.");
for(int i=0; i<v2.size(); i++)
ofs2 << v2[i];
ofs2.close();
keep_window_open();
return 0;
}
//********************************
catch(exception& e)
{
cerr << e.what() << endl;
keep_window_open();
return 0;
}
What is this?
while(ifs1.read(as_bytes(i), sizeof(int)));
It looks like a loop that reads all input and throws it away. The line afterward suggests that you should be using braces instead of a semicolon there, and doing the write in the block.
Your read and write operations aren't symmetric.
ifs1.read(as_bytes(i), sizeof(int))
grabs 4 bytes, and dumps the values into the char* its passed.
ofs1 << v1[i];
output the integer in v[i] as text. Those are very very different formats.
If you used >> to read you would have a lot more success.
To expound, the first read might look like this {'1','9','8','3'}, which I would guess would be the 959788084 you are seeing when you pun it to an int. Your second read would be {' ','1','3','6'}, like not what you'd hoped for either.
It's not clear (to me, at least), what you are trying to do.
When you say that the orginal file contains 1983 1262, what do
you really mean? That it contains two four byte integers, in
some unspecified format, whose values are 1983 and 1262? If so,
the problem is probably due to your machine not using the same
format. You cannot, in general, just read bytes (using
istream::read) and expect them to mean anything in your
machine's internal format. You have to read the bytes into
a buffer, and unformat them, according to the format with which
they were written.
Of course, opening a stream in binary mode doesn't mean that
the actual data are in some binary format; it just affects
things like how (or more strictly speaking, whether) line
endings are encoded, and how end of file is recognized.
(Strictly speaking, a binary file is not divided into lines. It
is just a sequence of bytes. Of course, some of those bytes
might have values that you, in your program, interpret and new
line characters.) If your file actually contains nine bytes
with characters corresponding to "1983 1362", then you'll have
to parse them as a text format, even if the file is written in
binary. You can do this by reading the entire file into
a string, and usingstd::istringstream; _or_, on most common
systems (but not necessarily on all exotics) by using>>` to
read, just as you would with a text file.
EDIT:
Just a simple reminder: you don't show the code for as_bytes,
but I'm willing to guess that there's a reinterpret_cast in
it. And any time you have to use a reinterpret cast, you can be
very sure that what you're doing isn't portable, and if it's
supposed to be portable, you're doing it wrong.

Create an array with external file in C++

I have 4 days of training in C++, so bear with me.
Two data files are required to evaluate a multiple-choice examination. The first file
(booklet.dat) contains the correct answers. The total number of questions is 50. A sample
file is given below:
ACBAADDBCBDDAACDBACCABDCABCCBDDABCACABABABCBDBAABD
The second file (answer.dat) contains the students’ answers. Each line has one student
record that contains the following information:
The student’s answers (a total of 50 answers) in the same format as above (* indicates no answer)., followed by Student ID and Student Name. Example:
AACCBDBC*DBCBDAAABDBCBDBAA*BCBDD*BABDBCDAABDCBDBDA 6555 MAHMUT
CBBDBC*BDBDBDBABABABBBBBABBABBBBD*BBBCBBDBABBBDC** 6448 SINAN
ACB*ADDBCBDDAACDBACCABDCABCCBDDABCACABABABCBDBAABD 6559 CAGIL
I have a homework assignment to write a C++ program that counts the total number of correct answers by each student and outputs this information to another file called report.dat. In this file, the student’s IDs, names and scores must be given. Each correct answer is worth 1 point. For the sample files given above, the output should be as follows:
6555 MAHMUT 10
6448 SINAN 12
6550 CAGIL 49
Here's what I have so far:
include <iostream>
include <fstream>
using namespace std;
int main()
{
char booklet[50] answers[50]
int counter
// Link answers with booklet.dat
booklet = ifstream
input_file("booklet.dat");
return 0;
// Link answers with answers.dat
answers = ifstream
input_file("answer.dat");
return 0;
while (booklet==answers)
{
counter++
cout << "The student had">>counter>> "answers right";
}
}
I'm not even sure I am in the correct direction. I know I need to create an array from the file booklet.dat and another one from the file answer.dat. Then the comparison has to be made and the matches between the two have to be counted.
I don't expect anyone to do the assignment for me, i just need a nudge in the right direction.
1.) On your Syntax:
a) Each line in C++ has to end with an ";". There are some lines in your excample which don't. (Normally your compile should point at this or the following line with an error)
b) Multiple variable definitions need a "," in between two different variables.
2.) I would recommend you to use something like that:
(have a look at C++ Reference fstream)
EDIT: just a little outline, which is not complete in this form, just to give you and idea ;-)
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
int nr_of_students = 1000; /* Or any number you'd like to analyze */
int stud_nr[nr_of_students];
string stud_name[nr_of_students];
int stud_count[nr_of_students];
fstream in_out;
in_out.open("filename.dat",fstream::in); // fstream::in for reading from file
// fstream::out for writing to this file
if(in_out.is_open())
{
for(lines=0;(in_out>>answers && lines<nr_of_students);lines++)
{
in_out >> stud_nr[lines]; /* EDIT: sorry hat some index confusions here... */
in_out >> stud_name[lines];
stud_count[lines]=0;
for(int i=0;i<50;i++)
{
/* comparison between the booklet_array and the answers_array */
/* Count up the stud_count[lines] for each right comparison */
}
}
/* some simmilar code for the output-file */
}
else cout << "Error reading " << "filename.dat" << endl;
return 1;
}
3.) Your code would also get more performance with vectors.
A good Tutorial would be: Tutorial part I
and you find part 2 in the comments there
4.) you can achieve a more dynamic code with argc and argv**, just google for that
I hope these comments help you a little bit to carry on ;)
You are already on the right direction. Basically you want to load the answer key into an array for fast comparison and then you need to check the answers of each student and each time they get a correct answer you increment a counter and write the ID, name and score for each student. There are problems with your code such as missing semicolons.
Also please note that returning exits a function and that no statements after an unconditional return are executed, returning from main terminates your program.
The normal approach to open a file for reading is:
#include<fstream>
#include<string>
int main()
{
std::ifstream input_file("inputfilename");
// since the answer key is one line
// and each students answer , id and name are also one line
// getting that line using std::getline() would be sufficient
std::string line;
std::getline(input_file, line);
// line would now contain the entire first line except the newline character
std::getline(input_file, line);
//now line would now contain the second line in the file
return 0;
}
Writing to a file is similar we use ofstream to open a file for writing.
Like so:
#include<fstream>
int main()
{
std::ofstream output_file("outputfilename");
// lets say we have a string and an int that we want to write
std::string line_to_write("Hello File");
int number = 42;
output_file << line_to_write << number; // writes the string and then 42 on the same line
output_file << '\n'; // writes the newline character so that next writes would appear on another line
return 0;
}
For references to the standard library and C++ in general when you need to know the available functions to do something I recommend cppreference here are the specific pages on ifstream and ofstream.

What's the correct way to read a text file in C++?

I need to make a program in C++ that must read and write text files line by line with an specific format, but the problem is that in my PC I work in Windows, and in College they have Linux and I am having problems because of line endings are different in these OS.
I am new to C++ and don't know could I make my program able read the files no matter if they were written in Linux or Windows. Can anybody give me some hints? thanks!
The input is like this:
James White 34 45.5 10 black
Miguel Chavez 29 48.7 9 red
David McGuire 31 45.8 10 blue
Each line being a record of a struct of 6 variables.
Using the std::getline overload without the last (i.e. delimiter) parameter should take care of the end-of-line conversions automatically:
std::ifstream in("TheFile.txt");
std::string line;
while (std::getline(in, line)) {
// Do something with 'line'.
}
Here's a simple way to strip string of an extra "\r":
std::ifstream in("TheFile.txt");
std::string line;
std::getline(input, line));
if (line[line.size() - 1] == '\r')
line.resize(line.size() - 1);
If you can already read the files, just check for all of the newline characters like "\n" and "\r". I'm pretty sure that linux uses "\r\n" as the newline character.
You can read this page: http://en.wikipedia.org/wiki/Newline
and here is a list of all the ascii codes including the newline characters:
http://www.asciitable.com/
Edit: Linux uses "\n", Windows uses "\r\n", Mac uses "\r". Thanks to Seth Carnegie
Since the result will be CR LF, I would add something like the following to consume the extras if they exist. So once your have read you record call this before trying to read the next.
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
If you know the number of values you are going to read for each record you could simply use the ">>" method. For example:
fstream f("input.txt" std::ios::in);
string tempStr;
double tempVal;
for (number of records) {
// read the first name
f >> tempStr;
// read the last name
f >> tempStr;
// read the number
f >> tempVal;
// and so on.
}
Shouldn't that suffice ?
Hi I will give you the answer in stages. Please go trough in order to understand the code.
Stage 1: Design our program:
Our program based on the requirements should...:
...include a definition of a data type that would hold the data. i.e. our
structure of 6 variables.
...provide user interaction i.e. the user should be able to
provide the program, the file name and its location.
...be able to
open the chosen file.
...be able to read the file data and
write/save them into our structure.
...be able to close the file
after the data is read.
...be able to print out of the saved data.
Usually you should split your code into functions representing the above.
Stage 2: Create an array of the chosen structure to hold the data
...
#define MAX 10
...
strPersonData sTextData[MAX];
...
Stage 3: Enable user to give in both the file location and its name:
.......
string sFileName;
cout << "Enter a file name: ";
getline(cin,sFileName);
ifstream inFile(sFileName.c_str(),ios::in);
.....
->Note 1 for stage 3. The accepted format provided then by the user should be:
c:\\SomeFolder\\someTextFile.txt
We use two \ backslashes instead of one \, because we wish it to be treated as literal backslash.
->Note 2 for stage 3. We use ifstream i.e. input file stream because we want to read data from file. This
is expecting the file name as c-type string instead of a c++ string. For this reason we use:
..sFileName.c_str()..
Stage 4: Read all data of the chosen file:
...
while (!inFile.eof()) { //we loop while there is still data in the file to read
...
}
...
So finally the code is as follows:
#include <iostream>
#include <fstream>
#include <cstring>
#define MAX 10
using namespace std;
int main()
{
string sFileName;
struct strPersonData {
char c1stName[25];
char c2ndName[30];
int iAge;
double dSomeData1; //i had no idea what the next 2 numbers represent in your code :D
int iSomeDate2;
char cColor[20]; //i dont remember the lenghts of the different colors.. :D
};
strPersonData sTextData[MAX];
cout << "Enter a file name: ";
getline(cin,sFileName);
ifstream inFile(sFileName.c_str(),ios::in);
int i=0;
while (!inFile.eof()) { //loop while there is still data in the file
inFile >>sTextData[i].c1stName>>sTextData[i].c2ndName>>sTextData[i].iAge
>>sTextData[i].dSomeData1>>sTextData[i].iSomeDate2>>sTextData[i].cColor;
++i;
}
inFile.close();
cout << "Reading the file finished. See it yourself: \n"<< endl;
for (int j=0;j<i;j++) {
cout<<sTextData[j].c1stName<<"\t"<<sTextData[j].c2ndName
<<"\t"<<sTextData[j].iAge<<"\t"<<sTextData[j].dSomeData1
<<"\t"<<sTextData[j].iSomeDate2<<"\t"<<sTextData[j].cColor<<endl;
}
return 0;
}
I am going to give you some exercises now :D :D
1) In the last loop:
for (int j=0;j<i;j++) {
cout<<sTextData[j].c1stName<<"\t"<<sTextData[j].c2ndName
<<"\t"<<sTextData[j].iAge<<"\t"<<sTextData[j].dSomeData1
<<"\t"<<sTextData[j].iSomeDate2<<"\t"<<sTextData[j].cColor<<endl;}
Why do I use variable i instead of lets say MAX???
2) Could u change the program based on stage 1 on sth like:
int main(){
function1()
function2()
...
functionX()
...return 0;
}
I hope i helped...