Unexpected output after reading from a binary file in turbo c++ - c++

After I have written into a file when reading it I get unexpected output.
The code I wrote is :
#include<fstream.h>
#include<conio.h>
#include<string.h>
struct test
{
char que[100];
char ans[20];
};
int main()
{
test s, d;
clrscr();
ofstream out("test.dat", ios::binary | ios::app);
ifstream in("test.dat", ios::binary | ios::in);
strcpy(s.que, "2.How many ways the letters of the word abas be arranged to form words with or without meaning");
strcpy(s.ans, "180");
out.write((char*) &s, sizeof(test));
while(!in.eof())
{
in.getline((char*) &d, sizeof(test));
cout << d.que << '\n' << d.ans;
}
getch();
return 0;
}
The output that I get is :
2.How many ways the letters of the word abas be arranged to form words with or w
ithout meaning
180
180
This is the output I get along with some arbitrary characters in between.
What have I done wrong? Why is the string that I stored in s.ans written into s.que as well?

I got this to work with the following changes:
Add out.close() after writing to the file to flush the output buffer.
Replace getline with read, to retrieve the bytes as write wrote them. getline might give different results under some conditions.
Move the read statement into the while condition:
while(in.read((char *) &d, sizeof(test)))
With the read statement in the body of the loop in.eof() will not return true immediately after reading the last test object so the loop will then execute one last time.

Related

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

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.

why reading one character from the file using char variable eating one more next character?

In the following program i am inputting data form file,
file contains : 1st line: answer key, subsequent line contains students ID and their answer response.
in the program i am comparing answer key and student response key and calculating grade and marks.
after reading id i need to discard space after the id, for that i need to read one character but while reading one character from the file with char variable it is reading space but also eating away next character so i am left with 9 char length student answer key, but it should be 10 char long.
and the while loop inside int main() which contain display function should run only one time because i have only one record in the file, but it is running 2 times.
what is wrong in this?
#include<iostream>
#include<string>
#include<fstream>
#include<iomanip>
using namespace std;
class student{
string ans,usn;
int marks,tot;
char grade;
public:
void input(string key,fstream &fp){
char ch;
marks=tot=0;
grade='z';
fp>>usn>>ch;
getline(fp,ans);
string::iterator it1,it2;
for(it1=key.begin(),it2=ans.begin()+1;it1!=key.end();++it1,++it2){
tot+=2;
if(*it1==*it2)
marks+=2;
else if(*it1!=*it2&&*it2!=' ');
//marks-=1;
}
grade=marks<0?'z':(75-(marks*10/tot));
}
void display(){
cout<<left<<setw(20)<<usn<<setw(20)<<ans<<setw(10)<<marks<<setw(10)<<grade<<ans.length()<<" "<<tot<<endl;
}
};
int main(){
student s;
string key;
char ch;
fstream fp("file.txt");
if(!fp){
cout<<"\nError in opening file"<<endl;
exit(0);
}
getline(fp,key);
cout<<left<<"key:"<<key<<endl<<setw(20)<<"usn"<<setw(20)<<"Answer key"<<setw(10)<<"marks"<<setw(10)<<"grade"<<endl<<setfill('-')<<setw(60)<<"-"<<setfill(' ')<<endl;
while(!fp.eof()){
s.input(key,fp);
s.display();
}
fp.close();
return 0;
}
file:
TTTTTTTTTT
1rv16is089 FTTTTTTTTT
output:
key:TTTTTTTTTT
usn Answer key marks grade
------------------------------------------------------------
1rv16is089 TTTTTTTTT 16 C 9 20
1rv16is089 TTTTTTTTT 16 C 9 20
Unless you have specifically asked the stream not to (noskipws) all the operator>> in the standard library will automatically skip all leading whitespace before starting to read its input.
So your fp >> ch will first skip all spaces and then read one more character.
There is a special manipulator std::ws that can be used to eat whitespace but not anyhting else. Just do fp >> std::ws;.
However, you also have a few other issues. When mixing >> and std::getline you might consider this, which probably is the base problem you tried to solve:
Why does std::getline() skip input after a formatted extraction?
And your problem with main() seeming to read one record too many is explained here:
Why is iostream::eof inside a loop condition considered wrong?

How to read and write in file with `fstream` simultaneously in c++?

I have the following code
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main(void) {
fstream ofile;
ofile.open("test.txt", ios::in | ios::out | ios::app);
for(string line; getline(ofile, line) ; ) {
cout << line << endl;
}
ofile << "stackexchnange" << endl;
ofile.close();
return 0;
}
test.txt contains
hello world!
stackoverflow
Above code outputs
hello world!
stackoverflow
And after running the code stackexchange is not appending at the end of test.txt. How to read and then write in file?
Nawaz' comment is correct. Your read loop iterates until the fstream::operator bool (of ofile) returns false. Therefore, after the loop, either failbit or badbit must have been set. failbit is set when loop tries to read for the final time but only EOF is left to read. This is completely OK, but you must reset the error state flag before trying to use the stream again.
// ...
ofile.clear();
ofile << "stackexchnange" << endl;
fstream has two positions : input and output.
In your case they both are set to the beginning of the file when you open it.
So you have 4 methods:
tellp // returns the output position indicator
seekp // sets the output position indicator
tellg // returns the input position indicator
seekg // sets the input position indicator
in your case you can use the following line to set output position to the end of the file
ofile.seekp(0, std::ios_base::end);
PS
i missed ios::app flag. mea culpa. comment of #Nawaz gives the right answer: after reading the whole file it is necessary to call
ofile.clear(); //cleanup error and eof flags

File read and write while reading the file line by line

Program:
#include<iostream>
#include<fstream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
FILE *f;
char* line;
size_t ln=100;
char* s;
line=new char[100];
s=new char[100];
cout<<"input key"<<endl;
cin>>s;
f=fopen("parvin.txt","r");
if(f==NULL)
{
cout<<" no file TO read so creating for writing "<<endl;
//return 0;
f=fopen("parvin.txt","w");
fputs(s,f);
fputc('\n',f);
}
else
{
while(! feof(f))
{
fgets(line,100,f);
cout<<line<<endl;
//if(!strncmp(line,s,strlen(line)-1))
if(strcmp(line,s)== 0 )
{
cout<<"duplicate found"<<endl;
fclose(f);
return 0;
}
}
fclose(f);
f=fopen("parvin.txt","a+");
fputs(s,f);
fputc('\n',f);
}
fclose(f);
}
Here the above program where I like to read an input string and write it into file provided the string is not present already in file.
take input string
open file in read mode.
if it is first time entry file will not be there if file pointer return NULL, create a file to write mode and write the
inputted string.
if file already there then read file line by line and compare with input string if match with any line then return and close.
other wise open the same file in write mode and write the inputted string.
But it is not working properly..
strcmp not executing properly.... with the duplicate entry also it
dont go into that loop of "duplicae found" .
please if anyone can help ...
The fgets:
fgets(line,100,f);
consumes the newline character from f and stores it in line. But s doesn't contain the newline character. So, the strcmp returns a non-zero number as the strings(s and f) are different.
Strip the newline character by using
line[strcspn(line, "\n")] = '\0';
just after the fgets. The strcspn function, in your case, returns the number of characters until a \n in line. If \n is not found in line, it returns the length of the string line(strlen(line)).
Also, read Why is while ( !feof (file) ) always wrong?. Replace
while(!feof(f))
with
while(fgets(line,100,f)) //Same as `while(fgets(line,100,f) != NULL)`
and don't forget to remove the fgets from the body of the loop to fix this issue.
Use
while(fgets(line,100,f)!=NULL)

String concatenation issue in C++

I am trying to concatenate some string but it works in one but not another.
Working: I take in 2 argument and then do this. a = hello, b = world
string concat = a + b;
The output would be hello world with no problem.
Not working: I read from file and concatenate with 2nd argument. assuming string from file is abcdefg.
string concat = (string from file) + b;
and it gives me worldfg.
Instead of concatenating, string from b overwrites the initial string.
I have tried a few other methods such as using stringstream but it doesn't work as well.
This is my code.
int main (int nArgs, char *zArgs[]) {
string a = string (zArgs [1]);
string b = string (zArgs [2]);
string code;
cout << "Enter code: ";
cin >> code;
string concat = code + b;
}
// The output above gives me the correct concatenation.
// If I run in command prompt, and do this. ./main hello world
// then enters **good** after the prompt for code.
// The output would be **goodworld**
However, I read some lines from the file.
string f3 = "temp.txt";
string r;
string temp;
infile.open (f3.c_str ());
while (getline (infile, r)) {
// b is take from above
temp = r + b;
cout << temp << endl;
}
// The above would give me the wrong concatenation.
// Say the first line in temp.txt is **quickly**.
// The output after reading the line and concatenating is **worldly**
Hope it gives more clear example.
Update:
I think I may have found out that the problem is due to the text file. I tried to create a new text file with some random lines inside, and it seem working fine. But if I try to read the original file, it gives me the wrong output. Still trying to put my head around this.
Then I tried to copied the content of the original file to the new file, and it seem to be working fine. Not too sure what is wrong here though. Will continue to test out, and hopefully it works fine.
Thanks for all the help! Appreciate it!
I get the same output as the chap who asked the original question:
$ ./a.out hello world
Enter code: good
goodworld
worldly
The problem here is the contents of the text file. For my example, the initial 7 characters in the text file are: "quickly". However, immediately following that are 7 backspace bytes (hex 08). This is what the contents looks like in emacs:
quickly^H^H^H^H^H^H^H
So how is this causing the mess?
Well the concatenation operation actually works correctly. If you do:
std::cout << "string length: " << temp.size() << "\n";
...you get the answer 19 which is made up of: "quickly" (7) + 7 backspace chars + "world"(5). The overwriting effect you observe is caused when you print this 19 char string to console: it is the console (eg xterm) that interprets the backspace sequence as meaning "move the cursor back to the left", thus removing earlier characters. If instead you pipe the output to file, you will see the full string (including the backspaces) is actually generated.
To get around this you might want to validate/correct the input that comes from the file. There are functions commonly available in C/C++ environments such as isprint(int c), iscntrl(int c) that you could make use of.
Update: as mentioned by another responder, other ASCII control characters will also have the same effect, eg, a carriage return (Hex 0D) will also move the cursor back to the left.
If I compile this
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main (int nArgs, char *zArgs[]) {
string a = string (zArgs [1]);
string b = string (zArgs [2]);
string code;
cout << "Enter code: ";
cin >> code;
string concat = code + b;
// The output above gives me the correct concatenation.
//However, I read some lines from the file.
ifstream infile;
string f3 = "temp.txt";
string r;
string temp;
infile.open (f3.c_str ());
while (getline (infile, r)) {
temp = r + code;
cout << temp << endl;
}
// The above would give me the wrong concatenation.
infile.close();
return 0;
}
it compiles and runs flawlessly. What does this do on your computer? If it fails, we may have to compare the contents of our temp.txt.
(This ought to be a comment rather than an answer, but it's too long. Sorry.)