I have a simple C++ file read program:
int main() {
std::string buf;
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
file.open("C:\\Test11.txt");
char c;
while (!(file.eof())) {
file.get(c);
std::cout << c;
}
}
catch (std::ifstream::failure e) {
std::cout << e.what() << std::endl;
std::cout << e.code() << std::endl;
std::cout << "Exception opening/reading file";
}
file.close();
return 0;
}
The content of file at C:\Test11.txt is Hello.
The program is able to read the content but fails with exception ios_base::failbit. It seems to have problem while evaluating while (!(file.eof())).
What is going wrong?
The issues is that eof() is not true until you attempt to read past the end of file.
So once you have correctly read the whole of the file and there is no data left in the file. The result of eof() is still false (as you have not read past the end). It is the next read that will set eof() but it is also the next read that will fail (and thus throw an exception).
This is why checking for eof() is an anti-pattern.
while (!(file.eof()))
{
file.get(c);
std::cout << c;
}
The better (correct) pattern is:
while (file.get(c)) // reads next character into c returns a ref to file.
// Note: when a stream is used in a boolean context
// such as an while () it is converted to bool
// using !good(). Thus if the read fails
// and the bad bit is set and the loop will exit.
{
std::cout << c;
}
Related
I'm trying to dump the contents of a file to cout.
#include <iostream>
#include <fstream>
int main(int argc, char** argv)
{
if (argc > 1) {
std::ifstream fin(argv[1]);
if (fin) {
std::cout << "---file contents---\n";
std::cout << fin.rdbuf();
std::cout << "---end contents---\n";
} else {
std::cout << "The file does not exist\n";
}
}
else {
std::cout << "Usage: " << argv[0] << " FILE\n";
}
if (std::cout.good()) {
return 0;
}
else if (std::cout.fail()) {
return 1;
}
else {
return 2;
}
}
This code does not work as intended when the input file is empty. It prints the initial "---file contents---", but never prints the trailing "---end contents---". After debugging, I found the application is not crashing, but instead is putting std::cout in an error state (the return code is 1).
How can I print the contents of an empty file without putting cout in an error state?
This operator<< reference (overload number 10 in the list) explains it all:
If no characters were inserted, executes setstate(failbit).
Since the input file is empty, there's no characters to insert into the output stream. And the failbit is set.
You need to add a specific check for failbit after
std::cout << fin.rdbuf();
to see if the input file was empty or not.
Originally was trying to read data using char* but switched to string cause was getting behavior as if there was a missing null terminator. made the problem minimal below but still getting very weird output
int main(int argc, char * argv[]){
// file one: flightData
std::ifstream inFile1(argv[1]);
if (!inFile1.is_open())
{
std::cout << "Could not open the file 1." << std::endl;
return -1;
}
// file two: flightPlans
std::ifstream inFile2(argv[2]);
if (!inFile2.is_open())
{
std::cout << "Could not open the file 2." << std::endl;
return -1;
}
//File three: output
std::ofstream outputfile(argv[3]);
if (!outputfile.is_open())
{
std::cout << "Could not open the output file" << std::endl;
return -1;
}
std::string buffer;
getline(inFile1, buffer);
std::cout<<buffer<<std::endl;
while (getline(inFile1, buffer)) {
std::cout<<buffer;
std::cout<<"help";
}
// flightPlanner system(inFile1);
// system.printF();
// system.planFlights(inFile2,outputfile);
return 0;
}
output is
4
helpDallas|Austin|50|50help
which i'm pretty sure is incorrect, interestingly when i add endl to cout buffer it gives me output i would expect not really sure whats going on
inFile1
4
Dallas|Austin|50|50
Dallas|Austin|50|50
Dallas|Austin|50|50
Dallas|Austin|50|50
When i run in debugger i get the output i expect:
4
Dallas|Houston|50|50
helpDallas|Houston|50|50
helpDallas|Houston|50|50
helpDallas|Houston|50|50help
any idea what could be going on?
Do you need flushing your stdout?
std::cout << std::flush;
Any chance your shell ate your outputs?
Try pipping the output to "cat -A":
./a.out | cat -A
(Drive by comment - I may not know what I'm talking about ^_^)
I would like to update existing json file.
This is example json file:
{
"Foo": 51.32,
"Number": 100,
"Test": "Test1"
}
Logs from program:
Operation successfully performed
100
"Test1"
51.32
46.32
Done
Looks like everythink works as expected...
If I change fstream to ifstream to read and later ofstream to write it's working...
I tried use debugger and as I see I have wrong data in basic_ostream object... but I dont know why, I use data from string with corrected (updated data).
Any idea what is wrong :-) ?
You have a few problems here.
First the command json json_data(fs); reads to the end of the file setting the EOF flag. The stream will stop working until that flag is cleared.
Second the file pointer is at the end of the file. If you want to overwrite the file you need to move back to the beginning again:
if (fs.is_open())
{
json json_data(fs); // reads to end of file
fs.clear(); // clear flag
fs.seekg(0); // move to beginning
Unfortunately that still doesn't fix everything because if the file you write back is smaller than the one you read in there will be some of the old data tagged to the end of the new data:
std::cout << "Operation successfully performed\n";
std::cout << json_data.at("Number") << std::endl;
std::cout << json_data.at("Test") << std::endl;
std::cout << json_data.at("Foo") << std::endl;
json_data.at("Foo") = 4.32; // what if new data is smaller?
Json file:
{
"Foo": 4.32, // this number is smaller than before
"Number": 100,
"Test": "Test1"
}} // whoops trailing character from previous data!!
In this situation I would simply open one file for reading then another for writing, its much less error prone and expresses the intention to overwrite everything.
Something like:
#include "json.hpp"
#include <iostream>
#include <fstream>
#include <string>
using json = nlohmann::json;
void readAndWriteDataToFile(std::string fileName) {
json json_data;
// restrict scope of file object (auto-closing raii)
if(auto fs = std::ifstream(fileName))
{
json_data = json::parse(fs);
std::cout << "Operation successfully performed\n";
std::cout << json_data.at("Number") << std::endl;
std::cout << json_data.at("Test") << std::endl;
std::cout << json_data.at("Foo") << std::endl;
}
else
{
throw std::runtime_error(std::strerror(errno));
}
json_data.at("Foo") = 4.32;
std::cout << json_data.at("Foo") << std::endl;
std::string json_content = json_data.dump(3);
if(auto fs = std::ofstream(fileName))
{
fs.write(json_content.data(), json_content.size());
std::cout << "Done" << std::endl;
}
else
{
throw std::runtime_error(std::strerror(errno));
}
}
int main()
{
try
{
std::string fileName = "C:/new/json1.json";
readAndWriteDataToFile(fileName);
}
catch(std::exception const& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
I open two files, one input and one output. I'd like to handle exceptions for both of them, so by looking at some examples, I made this:
std::ifstream readFile;
readFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
//set the flags for stream bits that indicate failure if ON
std::ofstream writeFile;
writeFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
try{
readFile.open(inputFileName);
writeFile.open(outputFileName);
function(readFile, writeFile);
readFile.close();
writeFile.close();
}
catch(std::ifstream::failure &readErr) {
std::cerr << "\n\nException occured when reading a file\n"
<< readErr.what()
<< std::endl;
return -1;
}
catch(std::ofstream::failure &writeErr) {
std::cerr << "\n\nException occured when writing to a file\n"
<< writeErr.what()
<< std::endl;
return -1;
}
This seems like a reasonable solution, but I get a warning:
warning: exception of type 'std::ios_base::failure' will be caught [enabled by default]
catch(std::ofstream::failure &writeErr) {
^
The code does it's thing, but I'm still interested in improving my code. Where have I wronged?
No you can't. The typedef of std::ifstream::failure and std::ofstream::failure are both defined to be std::ios_base::failure.
The best thing you could do is wrap the individual calls with try-catch:
try
{
readFile.open(inputFileName);
}
catch(std::ifstream::failure &readErr)
{
}
try
{
writeFile.open(outputFileName);
}
catch(std::ofstream::failure &writeErr)
{
}
Or check the state of the streams individually in the catch block to see who failed.
The only way to handle exceptions from the two files separately would be to catch the exception and then check the failbit on the streams to determine which of them that failed:
try
{
readFile.open(inputFileName);
writeFile.open(outputFileName);
function(readFile, writeFile);
readFile.close();
writeFile.close();
}
catch (const std::ios_base::failure &err)
{
if (readFile.fail())
{
std::cerr << "\n\nException occured when reading a file\n"
<< readErr.what()
<< std::endl;
}
if (writeFile.fail())
{
std::cerr << "\n\nException occured when writing to a file\n"
<< writeErr.what()
<< std::endl;
}
return -1;
}
string mapFile;
cout << "Enter the file name : ";
cin >> mapFile;
ifstream mapfh;
mapfh.open(mapFile.c_str());
if(mapfh.is_open()) { ... }
else //if board file did not open properly
{
throw;
}
mapfh.close();
I am compiling with g++ in the command line. Whenever I put a file input (even with a full path i.e. /User/...etc./file.txt) it throws an error. I know the input is good, but for whatever reason the open always fails.
This isn't fully portable, but you'll get a more informed output if you interpret the errno,
#include <cerrno>
#include <cstring>
...
if(mapfh.is_open()) { ... }
else //if board file did not open properly
{
std::cout << "error: " << strerror(errno) << std::endl;
throw;
}
And if your policy is to communicate the errors as exceptions then use iostreams native support for the exceptions:
ifstream mapfh;
mapfh.exceptions(std::ios::failbit);
try {
mapfh.open(mapFile.c_str());
...
mapfh.close();
} catch (const std::exception& e) {
std::cout << e.what() << " : " << std::strerror(errno) << std::endl;
}