I have written a small C++ program to set a property in a text file. The implementation is as following:
#include <cstdio>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
const string PROPFILE = "./propfile";
const string TEMPFILE = PROPFILE + ".tmp";
int setProp(const string &key, const string &val) {
try {
ifstream original(PROPFILE, ios::in);
ofstream tempfile(TEMPFILE, ios::out | ios::trunc);
for (string line; getline(original, line); ) {
if (line.compare(0, key.size(), key) == 0 && line[key.size()] == '=') {
tempfile << key << '=' << val << endl;
} else {
tempfile << line << endl;
}
}
cout << "original.rdstate()" << original.rdstate() << endl;
original.close();
tempfile.close();
} catch (ifstream::failure e) {
cerr << e.what() << endl;
}
if (rename(TEMPFILE.c_str(), PROPFILE.c_str()) != 0) {
cout << "Could not move " + TEMPFILE << "to " << PROPFILE << endl;
return 1;
}
return 0;
}
int main(int argc, const char *argv[]) {
try {
return setProp(argv[1], argv[2]);
} catch (logic_error) {
cout << "Invalid args" << endl;
return 1;
}
}
However, when I try to compile and execute it from commandline via ./a.out TESTPROP TESTVALUE, the value IS set as expected in propfile but rdstate() returns 6 (which means failbit and eofbit are set), I can't understand why are they getting set, can somebody explain ?
Contents of propfile before running ./a.out TESTPROP TESTVALUE are:
TESTPROP=NOTHING
After running the progam:
TESTPROP=TESTVALUE
I'm just a student, please don't mind if it's a dumb question :)
This is expected behaviour, the failbit is set whenever there is a failure to read the expected value. Even if that failure is because of end of file.
For instance see here
If no characters were extracted for whatever reason (not even the
discarded delimiter), getline sets failbit and returns.
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.
my code work good when the argc is 1 but when I try to read and write from files (when argc is 3)
the program not working well. Gcalc get the ostream (output file or cout) and current line in input file
or cin and decode the string to command on gcalc data.
#include <ostream>
#include <fstream>
#include <string>
#include <iostream>
#include "Gcalc.h"
using namespace std;
int main(int argc, char* argv[]) {
Gcalc gcalc;
string current_line;
ifstream input;
ofstream output;
if (argc != 1 && argc != 3) {
return 0;
}
if (argc == 3) {
input = ifstream(argv[1]);
cin.rdbuf(input.rdbuf());
output = ofstream(argv[2]);
cout.rdbuf(output.rdbuf());
}
while (cin.good()) {
if (argc == 1) {
cout << "Gcalc> ";
}
getline(cin, current_line);
try {
gcalc.implementCommand(cout, current_line);
}
catch (Quit_Program& error) {
break;
}
catch (std::bad_alloc& error) {
std::cerr << "Error: fatal error - bad allocation" << endl;
break;
}
catch (Exception& error) {
cout << error.what() << endl;
}
}
return 0;
}
Check that opening the files was done successfully.
Check that the istream you read from doesn't have the failbit set after you've read from it. Since an istream in a boolean context checks badbit and failbit and that std::getline returns the same istream you gave it, replace your while (cin.good()) with:
while(getline(cin, current_line)) {
// ... only entered if badbit and failbit are false ...
}
That said, it's usually better to create a separate function for reading/writing to generic istream/ostreams. This way you don't have to mess with the rdbufs of cin and cout.
#include <fstream>
#include <iostream>
#include <string>
#include "Gcalc.h"
void do_stuff(std::istream& is, std::ostream& os) {
Gcalc gcalc;
std::string current_line;
while(getline(is, current_line)) {
try {
gcalc.implementCommand(os, current_line);
} catch(Quit_Program& error) {
break;
} catch(const std::bad_alloc& error) {
std::cerr << "Error: fatal error - " << error.what() << std::endl;
break;
} catch(Exception& error) {
std::cout << error.what() << std::endl;
// or, if you really want it:
// os << error.what() << std::endl;
}
}
}
int main(int argc, char* argv[]) {
if(argc == 1) {
do_stuff(std::cin, std::cout);
} else if(argc == 3) {
std::ifstream input(argv[1]);
std::ofstream output(argv[2]);
if(input && output) do_stuff(input, output);
}
}
If you want to give the user a prompt when the program is running in interactive mode, you could add a function that prints the prompt and then calls std::getline. You can combine this inside the while loop, but it looks messy, so I would suggest something like this:
std::istream& prompt(std::istream& is, std::string& line) {
if(&is == &std::cin) std::cout << "Gcalc> ";
return std::getline(is, line);
}
// ...
while(prompt(is, current_line)) {
// ...
}
With some help I've almost completed a program which enables me to extract the timestamps(eg:timestamp="2014-07-08T18:14:16.468Z" ) and only the timestamps from and XML file and output them to a designated output file. However, there are a handful of errors left in my code which have me at wits end, which can't seem to redress. Would someone more experienced with C++ mind helping me out?
The errors appear in lines 35,38, & 47.
Screenshot of errors: http://i.imgur.com/jVUig4T.jpg
Link to XML file: http://pastebin.com/DLVF0cXY
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
using namespace std;
string tempStr;
// escaped double qoute.
string findStr = "timestamp=\"";
ifstream inFile;
ofstream outFile;
outFile.open("Outputdata.txt");
inFile.open("Groupproject.xml");
if (inFile.fail()) {
cout << "Error Opening File" << endl;
system("pause");
exit(1);
}
size_t found;
while (inFile) {
getline(inFile, tempStr);
found = tempStr.find(findStr);
if (found != std::string::npos)
{
break;
}
}
// Erases from beggining to end of timestamp="
tempStr.erase(tempStr.begin(), (found + tempStr.length()));
// Finds index of next double qoute.
found = tempStr.findStr("\"");
if (found = std::string::npos)
{
cerr << "Could not find matching qoute:";
exit(1);
}
// Erases from matching qoute to the end of the string.
tempStr.erase(found, tempStr.end());
cout << "timestamp found" << tempStr << "Saving to outFile" << endl;
outFile << tempStr;
inFile.close();
outFile.close();
system("pause");
return 0;
}
Are you sure you carefully read the reference for all the functions you are using ? Your new friend
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
using namespace std;
int main()
{
string tempStr;
string findStr = "timestamp=\""; // escaped double quote
ifstream inFile;
ofstream outFile;
outFile.open( "Outputdata.txt" );
inFile.open( "Groupproject.xml" );
if ( inFile.fail() )
{
cout << "Error Opening File" << endl;
cin.get();
exit( 1 );
}
size_t found;
while ( inFile )
{
getline( inFile, tempStr );
cout << tempStr << endl;
found = tempStr.find( findStr );
if ( found != string::npos )
break;
}
tempStr.erase( 0, found + findStr.length() ); // erases from beggining to end of timestamp="
found = tempStr.find( "\"" ); // finds index of next double quote
if ( found == string::npos )
{
cerr << "Could not find matching quote" << endl;
exit( 1 );
}
tempStr.erase( found, string::npos ); // erases from matching quote to the end of the string.
cout << "timestamp found:" << tempStr << " Saving to outFile" << endl;
outFile << tempStr;
inFile.close();
outFile.close();
cin.get();
return 0;
}
I read about substr from here
http://www.cplusplus.com/reference/string/string/substr/
Here is my code :
int main()
{
std::ifstream in ("c:\\users\\admin\\desktop\\aaa.txt");
std::ofstream out ("c:\\users\\admin\\desktop\\bbb.txt");
std::string s ;
while ( getline (in,s) )
{
std::size_t startpos = s.find("test");
std::string str = s.substr (startpos);
out << str << endl;
}
in.close();
out.close();
}
I get error : R6010 abort() has been called
Note : aaa.txt contains spaces/characters/html tags
Any idea ?
Since I dont know the content of the text file, could you try making the following changes and let me know if the error is still being shown:
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
ifstream in("example.txt");
ofstream out("bbb.txt");
string s = std::string();
string str = std::string();
while (getline(in, s))
{
size_t startpos = s.find("test");
cout << s;
if (startpos != std::string::npos){
str = s.substr(startpos);
out << str << endl;
}
}
in.close();
out.close();
getchar();
return 0;
}
I am using if (startpos != std::string::npos) condition to check what to do when the find succeeds, this is missing in your code. adding this case will resolve your error.
Keep coding :)
While Code Frenzy answer is right, you can also use exceptions to help catch these kind of errors:
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
std::ifstream in ("aaa.txt");
std::ofstream out ("bbb.txt");
std::string s ;
try
{
while ( getline (in,s) )
{
std::size_t startpos = s.find("test");
std::string str = s.substr (startpos);
out << str << endl;
}
in.close();
out.close();
}
catch(std::exception e)
{
// (1) it will catch the error show show
cerr << e.what() << endl;
}
catch(std::out_of_range e)
{
// (2) this will also catch the same error if (1) was not there but could
// get you more details if you wanted since its more specific but i have
// not digged into it further
cerr << e.what() << endl;
}
catch(...)
{
// (3) just for sanity check if first two didn't catch it
cerr << "something went wrong";
}
}
The exceptoin catches this error and prints the message:
invalid string position
Using GCC 4.7.3 on Cygwin 1.7.24. Compiler options include: -std=gnu++11 -Wall -Wextra
I am working on a command line application and I needed to be able to load and save a set of strings so I wrote a quick wrapper class around std::set to add load and save methods.
// KeySet.h
#ifndef KEYSET_H
#define KEYSET_H
#include <cstdlib>
#include <sys/stat.h>
#include <cerrno>
#include <cstring>
#include <string>
#include <set>
#include <iostream>
#include <fstream>
inline bool file_exists (const std::string& filename)
{
/*
Utility routine to check existance of a file. Returns true or false,
prints an error and exits with status 2 on an error.
*/
struct stat buffer;
int error = stat(filename.c_str(), &buffer);
if (error == 0) return true;
if (errno == ENOENT) return false;
std::cerr << "Error while checking for '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
class KeySet
{
private:
std::string filename;
std::set<std::string> keys;
public:
KeySet() {}
KeySet(const std::string Pfilename) : filename(Pfilename) {}
void set_filename (const std::string Pfilename) {filename = Pfilename;}
std::string get_filename () {return filename;}
auto size () -> decltype(keys.size()) {return keys.size();}
auto cbegin() -> decltype(keys.cbegin()) {return keys.cbegin();}
auto cend() -> decltype(keys.cend()) {return keys.cend();}
auto insert(const std::string key) -> decltype(keys.insert(key)) {return keys.insert(key);}
void load ();
void save ();
};
void KeySet::load ()
{
if (file_exists(filename)) {
errno = 0;
std::ifstream in (filename, std::ios_base::in);
if (in.fail()) {
std::cerr << "Error opening '" << filename << "' for reading: " << strerror(errno) << std::endl;
exit (2);
}
std::string token;
if (token.capacity() < 32) token.reserve(32);
while (in >> token) keys.insert(token);
if (!in.eof()) {
std::cerr << "Error reading '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
in.clear(); // need to clear flags before calling close
in.close();
if (in.fail()) {
std::cerr << "Error closing '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
}
}
void KeySet::save ()
{
errno = 0;
std::ofstream out (filename, std::ios_base::out);
if (out.fail()) {
std::cerr << "Error opening '" << filename << "' for writing: " << strerror(errno) << std::endl;
exit (2);
}
for (auto key = keys.cbegin(), end = keys.cend(); key != end; ++key) {
out << *key << std::endl;
}
out.close();
if (out.fail()) {
std::cerr << "Error writing '" << filename << "': " << strerror(errno) << std::endl;
exit (2);
}
}
#endif
//
Here's a quick program to test the load method.
// ks_test.cpp
#include "KeySet.h"
int main()
{
KeySet test;
std::string filename = "foo.keys.txt";
test.set_filename(filename);
test.load();
for (auto key = test.cbegin(), end = test.cend(); key != end; ++key) {
std::cout << *key << std::endl;
}
}
The data file just has "one two three" in it.
When I go to run the test program, I get the following error from my test program:
$ ./ks_test
Error closing 'foo.keys.txt': No error
Both cppreference.com and cplusplus.com say that the close method should set the fail bit on error. The save method works fine, and the load method works correctly if I comment out the error check after the close. Should this really work or have I misunderstood how close is supposed to work? Thanks in advance.
Edited to clarify, fix typo's and adjust code per Joachim Pileborg's and Konrad Rudolph's comments.
Edited to add solution to the code.
You have two errors here: The first is about how you do your reading, more specifically the loop for reading. The eof flag will not be set until after you tried to read and the read failed. Instead you should do like this:
while (in >> token) { ... }
Otherwise you will loop one time to many and try to read beyond the end of the file.
The second problem is the one you notice, and it depends on the the first problem. Since you try to read beyond the end of the file, the stream will set failbit causing in.fail() to return true even though there is no real error.
As it turns out, the close method for ifstream (and I assume all other IO objects) DOES NOT clear the error flags before closing the file. This means you need to add an explicit clear() call before you close the stream after end of file if you are checking for errors during the close. In my case, I added in.clear(); just before the in.close(); call and it is working as I expect.