In what practical case bool(std::ifstream) != std::ifstream::good()? - c++

I would like to know in what case we can have :
bool(std::ifstream) != std::ifstream::good()
The difference is that bool(std::ifstream) does not test the eof bit whereas std::ifstream::good() tests it. But practically, the eof bit is raised if one try to read something after the end of the file. But as soon as you try to do this I think that either fail or bad bit is also set.
Consequently in what case you can only raise the eof bit ?

Simply put, whenever you encounter the end of a file without attempting to read behind it. Consider a file "one.txt" which contains exactly one single '1' character.
Example for unformatted input:
#include <iostream>
#include <fstream>
int main()
{
using namespace std;
char chars[255] = {0};
ifstream f("one.txt");
f.getline(chars, 250, 'x');
cout << f.good() << " != " << bool(f) << endl;
return 0;
}
0 != 1
Press any key to continue . . .
Example for formatted input:
#include <iostream>
#include <fstream>
int main()
{
using namespace std;
ifstream f("one.txt");
int i; f >> i;
cout << f.good() << " != " << bool(f) << endl;
return 0;
}
0 != 1
Press any key to continue . . .

Related

Why does tellp() give -1?

I'm trying to learn about fstream and here is the code that I'm running on VS2019:
#include <iostream>
#include <iomanip>
#include <string>
#include <fstream>
using namespace std;
int main() {
//cout << "lOOKING IN FILE";
string s;
fstream dictionary("C:/Users/source/repos/Test2/Test2/Text.txt");
if (!dictionary) // were there any errors on opening?
exit(-1);
while (dictionary >> s) cout << s << '\n'; // Print all names in file
dictionary.seekp(0, ios::beg); // Go back to beginning of file
cout << dictionary.tellp() << endl;
dictionary >> s;
cout << s; // Print the first name
return 0;
}
The output is:
abc
acb
cab
-1
cab
Why does tellp give -1 and not go to beginning of file?
You need to clear the state of the stream.
Once the state of a stream have changed from good (i.e. when it reaches end-of-file or there's a failure) then you can't operate on the stream again without clearing the state.

Issue printing .txt file in program C++

I have a program that takes a text file and list the words and how many times they are used. It works but I can't figure out how to print out the text file. Above the sorted words and how many times they appear, I want to display the text from the file. How would I do that? I tried several things but it either does nothing or screws up the rest of the code saying there are 0 unique words. And lastly how would print out the results so they are more ... table -ish...
/*
Something like this:
Word: [equal spaces] Count:
ask [equal spaces] 5
anger [equal spaces] 3
*/
Thank you for any assistance you can provide me.
#include <iterator>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <cctype>
using namespace std;
string getNextToken(istream &in) {
char c;
string ans="";
c=in.get();
while(!isalpha(c) && !in.eof())//cleaning non letter charachters
{
c=in.get();
}
while(isalpha(c))
{
ans.push_back(tolower(c));
c=in.get();
}
return ans;
}
string ask(string msg) {
string ans;
cout << msg;
getline(cin, ans);
return ans;
}
int main() {
map<string,int> words;
ifstream fin( ask("Enter file name: ").c_str() ); //open an input stream
if( fin.fail() ) {
cerr << "An error occurred trying to open a stream to the file!\n";
return 1;
}
string s;
string empty ="";
while((s=getNextToken(fin))!=empty )
++words[s];
while(fin.good())
cout << (char)fin.get(); // I am not sure where to put this. Or if it is correct
cout << "" << endl;
cout << "There are " << words.size() << " unique words in the above text." << endl;
cout << "----------------------------------------------------------------" << endl;
cout << " " << endl;
for(map<string,int>::iterator iter = words.begin(); iter!=words.end(); ++iter)
cout<<iter->first<<' '<<iter->second<<endl;
return 0;
}
I would just use a simple for loop like this:
for (int x = 0; x < words.size(); x++){
cout >> words[x] << endl
}
And then modify from there to get your desired format.
I did notice though, that you are not returning a value for main in all paths of the above code, which should give a compile time error, but did not when I compiled it, for some reason. I would remind you that you need to have a return value for main. Unless I am misunderstanding your question. I could not run this program without creating a sample file, and so could not test it without extra work. But the program did compile. I did not expect to, because of the missing return statement. If you can make this reproduce your error without me having to create a sample file of words, ei insert the list of words into the code and minimally reproduce the error, I would be able to help you better. As it is, I hope that I helped you.
Something like this should make it:
#include <iostream>
#include <fstream>
#include <unordered_map>
#include <string>
int main( int argc, char* argv[] )
{
std::string file;
std::cout << "Enter file name: ";
std::cin >> file;
std::fstream in( file.c_str() );
if ( in.good() )
{
std::unordered_map<std::string, int> words;
std::string word;
//Use this to separate your words it could be '\n' or anything else
char cSeparator = ' ';
while ( in >> word )
{
//Print the word
std::cout << word << cSeparator;
++words[word];
}
std::cout << std::endl;
//Headers Word and Count separated by 2 tabs
std::cout << "Word:\t\tCount:" << std::endl;
for ( auto& w : words )
std::cout << w.first << "\t\t" << w.second << std::endl;
}
in.close();
return EXIT_SUCCESS;
}
However this is assuming that the text file only contains the words, if you have other kind of stuff there, you should be able to filter it as you want.

Cannot Print All Integer Values

I would like to print integer values in a file by reading it.
The code:
int temp;
char* trainname;
trainname="dfg.txt";
ifstream trainfile;
trainfile.open(trainname);
if(!trainfile){
cout<<"Cannot open file!"<<'\n';
exit(1);
}
while(trainfile >> temp)
cout << temp << " ";
trainfile.close();
dfg.txt: 1 2 we er rf 5
output: 1 2
The problem is that it does not print 5.
Read to a temporary string first and then use std::stoi to try to parse an integer from it, and if it succeeds, output it:
std::string temp;
while(trainfile >> temp) {
try {
std::cout << std::stoi(temp) << " ";
}
catch(const std::invalid_argument&) {
// not a valid number
}
}
while(trainfile >> temp)
cout << temp << " ";
The above sets the failbit on trainfile on encountering any character that isn't whitespace or a digit. That terminates the loop. This is one reason I tend not to use formatted I/O that can fail on a input stream. I find it better to read text as text (not numbers) and then process the string that was just read. For example, see zenith's answer.
If you insist on doing everything from the input stream, you'll need an outer loop that clears the stream's failbit. For example,
while (! std::cin.eof())
{
while (std::cin >> temp)
{
std::cout << temp << " ";
}
std::cin.clear();
std::cin.ignore();
}
Given an input file containing 1 2 we er rf 5, the above will print 1 2 5. If the input file contains 1 2 abc345def 6, the above will print 1 2 345 6. Note that zenith's approach will print 1 2 6. Whether that 345 sandwiched between abc and def counts as an integer is up to you.
I'd recommend using zenith's solution over mine.
Update:
The above interprets abc345def as representing the integer 345. Both Zenith's solution and the above interpret 345def as representing the integer 345. To me, both abc345def and 345def should be rejected as representing an integer. So should 6.1 , but there's nothing wrong with 0x abc345def. There's nice tool in the C standard library, strtol, that nicely parses integers. It also indicates what made the parse stop. For a valid integer, it should stop at the end of the input string. With that,
#include <iostream>
#include < fstream>
#include <string>
#include <cstdlib>
int main ()
{
std::ifstream trainfile("dfg.txt");
if (!trainfile)
{
std::cerr << "Cannot open file!\n";
exit(1);
}
std::string s;
while(trainfile >> s)
{
char* end;
long num = std::strtol (s.data(), &end, 0);
if (!*end)
{
std::cout << num << " ";
}
}
trainfile.close();
std::cout << "\n";
}
string temp;
if( '0' <= temp[0] && temp[0]<='9' )
cout << temp << " ";
it will work i suppose.
Here is another way you can consider-
#include <iostream>
#include <sstream>
#include <fstream>
using namespace std;
int main()
{
ifstream trainname("dfg.txt",ios::in);
string temp;
getline(trainname,temp);
stringstream str;
str<<temp;
int extract_int;
while(getline(str, temp,' '))
{
if(stringstream(temp)>>extract_int)
cout<<extract_int<<" ";
}
return 0;
}
Or according to David Hammen's answer, you can solve it the following way-
#include <iostream>
#include <sstream>
#include <fstream>
using namespace std;
int main()
{
int temp;
char* trainname;
trainname="dfg.txt";
ifstream trainfile;
trainfile.open(trainname);
if(!trainfile){
cout<<"Cannot open file!"<<'\n';
exit(1);
}
while (!trainfile.eof())
{
while (trainfile>>temp)
cout<<temp<< " ";
trainfile.clear();
trainfile.ignore();
}
return 0;
}

How to read txt file into vector and cout the file?

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<int> temp;
ifstream infile;
infile.open("numbers");
if (infile.fail())
{
cout << "Could not open file numbers." << "\n";
return 1;
}
int data;
infile >> data;
while (!infile.eof()) {
temp.push_back(data);
infile >> data;
}
cout << data << " " << endl;
}
I am simply trying to cout all the numbers from the text file "numbers" using a vector.
15
10
32
24
50
60
25
My experience is pretty much nil, and some guidance on why this fails to open would be very helpful.
Your code isn't working because you haven't attempted to print anything from the vector?
How do I print a vector?
Well first you have to understand how not to print a vector. The last line in your code, particularly this one:
cout << data << " " << endl;
is only printing out the last integer from the text file. In the loop where you performed the input, infile >> data overwrote each previous value of data and assigned it to the currently read value from the file. The result is that when the loop finishes, data will be equal to the last read value, particularly 25 looking at your file.
There's no overload for operator<<() that will allow you to do something like cout << temp, though you can implement one yourself. There exist several ways to print a vector, the easiest being a simple loop:
for (unsigned i = 0; i < temp.size(); ++i)
std::cout << temp[i] << " ";
Bonus: A faster way to print all the integers would be to print data from inside the loop. There's also the answer #KerrekSB made.
Your code is fine but you're printing the wrong thing.
Change the bottom of main to this
int data;
while (infile >> data) {
temp.push_back(data);
}
for( vector<int>::iterator i = temp.begin(); i != temp.end(); i++) {
cout << *i << endl;
}
*Edited after reading the suggested dup.
Try this:
#include <fstream>
#include <iostream>
#include <iterator>
#include <vector>
int main()
{
std::ifstream infile("data.txt");
if (!infile) { /* error opening file */ }
for (int n : std::vector<int>(std::istream_iterator<int>(infile), {}))
{
std::cout << n << '\n';
}
}
Of course you don't need the vector if you just want to process the numbers:
for (std::istream_iterator<int> it(infile), end; it != end; ++it)
{
std::cout << *it << '\n';
}

C++ stringstream returning extra character?

I've been attempting to use the C++ stringstream class to do some relatively simple string manipulations, but I'm having a problem with the get() method. For some reason whenever I extract the output character by character it appends a second copy of the final letter.
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
stringstream ss("hello");
char c;
while(!ss.eof()) {
ss.get(c);
cout << "char: " << c << endl;
}
return 0;
}
The output from the program is:
char: h
char: e
char: l
char: l
char: o
char: o
Any help you can give me on this would be appreciated.
At the end of the stream ss.eof() doesn't know yet that the end of the stream will be reached soon, but the following extraction of a character fails. Since the extraction failed because the end of the stream was reached, c is not changed. Your program doesn't recognize that ss.get(c) failed and prints that old value of c again.
A better way to check if there still is a character that can be read from the stream would be a loop like this:
while (ss.get(c)) {
cout << "char: " << c << endl;
}
its because of the order of the loop. Your reading the \0 and EOF.
reorder your code like this
int main() {
stringstream ss("hello");
char c;
ss.get(c);
while(!ss.eof()) {
cout << "char: " << c << endl;
ss.get(c);
}
return 0;
}
The EOF flag is only set if you attempt to read PAST the end of the file. The following code fixes the problem by testing for EOF after the get() instead of before it:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
stringstream ss("hello");
char c;
while(1) {
ss.get(c);
if(ss.eof())
break;
cout << "char: " << c << endl;
}
return 0;
}