I am writing a config for my program. This config is stored in json format using nlohman json. I am using std::fstream to write json object to file. All was fine, but after some moment my program stopped writing to file.
Here is a minimal reproducible example:
#include <iostream>
#include <nlohmann/json.hpp>
#include <fstream>
#include <iomanip>
bool load(std::fstream &config_fs) {
if (config_fs) {
try {
nlohmann::json json;
config_fs >> json;
std::cout << json << std::endl;
return true;
} catch (std::exception &e) {
std::cout << "54 " << (bool)config_fs << std::endl; // 1
std::cout << "Cannot load config: " << e.what() << std::endl;
return false;
}
}
std::cout << "Cannot load config because file is not open" << std::endl;
return false;
}
bool save(std::fstream &config_fs) {
std::cout << "37 " << (bool)config_fs << std::endl;
if (config_fs) {
nlohmann::json json{{"test", 42}};
std::cout << "39 " << (bool)config_fs << " " << config_fs.is_open() << " " << strerror(errno) << std::endl;
config_fs << std::setw(4) << json << std::endl;
std::cout << "41 " << (bool)config_fs << " " << config_fs.is_open() << " " << strerror(errno) << std::endl;
return true;
}
std::cout << "Cannot save config because file is not open" << std::endl;
return false;
}
int main() {
std::string config_file = "../config_test.json";
std::fstream config_fs(config_file, std::ios::in | std::ios::out);
if (!config_fs) {
std::cout << "Cannot open configuration file " << config_file << ", creating it" << std::endl;
config_fs.open(config_file, std::ios::out);
if (!config_fs) {
std::cout << "Cannot create config file " << config_file << std::endl;
} else {
config_fs.close();
config_fs.open(config_file, std::ios::in | std::ios::out);
}
}
if(!load(config_fs)) {
std::cout << "Cannot load config from " << config_file << ", putting default values" << std::endl;
std::cout << "21 " << (bool)config_fs << std::endl;
save(config_fs);
}
return 0;
}
And here is output of the programm:
54 1
Cannot load config: [json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal
Cannot load config from ../config_test.json, putting default values
21 1
37 1
39 1 1 Success
41 0 1 Success
It means that fstream closes after my write attempt with Success errno and file stays empty!
I do not know what may cause such a behavior. I also could not find any similar questions and i really confused about that.
Please point me, what can be reason for this problem.
Thank you!
UPD: Exception 'std::ios_base::failure[abi:cxx11]'
what(): basic_ios::clear: iostream error was thrown in output_stream_adapter::write_characters(const CharType* s, std::size_t length) after enabling exceptions for fstream with config_fs.exceptions(std::ios::badbit | std::ios::failbit);
Related
I am trying to write a string to a binary file, to that effect I made this snippet:
std::string outname = filename.substr(0, filename.size() - 4) + std::string("_bin") + std::string(".sdf");
std::cout << "Writing results to: " << outname << "\n";
std::ofstream outfile(outname.c_str(), std::ofstream::binary);
std::stringstream ss;
ss << phi_grid.ni << " " << phi_grid.nj << " " << phi_grid.nk << std::endl;
ss << min_box[0] << " " << min_box[1] << " " << min_box[2] << std::endl;
ss << dx << std::endl;
for (unsigned int i = 0; i < phi_grid.a.size(); ++i)
{
ss << phi_grid.a[i] << std::endl;
}
outfile.write(ss.str().c_str(), ss.str().size());
outfile.close();
This however always writes to a text file, not a binary file. I don't udnerstand why, the flag is explicitely passed to the constructor.
fstream fs("f.txt", fstream::in | fstream::out | fstream::trunc);
if(fs)
{
string str = "45464748";
fs << str;
fs.seekg(0, ios::beg);
int i = -1;
fs >> i;
cout << i << endl;
fs.seekp(0, ios::beg);
i = 0x41424344;
fs << i;
fs.close();
}
f.txt content is "45464748",but I should understand it's content is "1094861636". I don't the reason, please help me.
The stream state has its eof bit set by the previous read, so the write has no effect. Clear the stream state before the write.
void ftest()
{
std::fstream fs("f.txt", std::fstream::in | std::fstream::out | std::fstream::trunc);
if(fs)
{
std::cout << "A: " << (fs.eof() ? "eof" : "neof") << std::endl;
std::string str = "45464748";
fs << str;
std::cout << "B: " << (fs.eof() ? "eof" : "neof") << std::endl;
fs.seekg(0, std::ios::beg);
std::cout << "C: " << (fs.eof() ? "eof" : "neof") << std::endl;
int i = -1;
// THIS read sets the EOF bit.
fs >> i;
std::cout << "D: " << (fs.eof() ? "eof" : "neof") << std::endl;
std::cout << i << std::endl;
fs.seekp(0, std::ios::beg);
std::cout << "E: " << (fs.eof() ? "eof" : "neof") << std::endl;
i = 0x41424344;
std::cout << "F: " << (fs.eof() ? "eof" : "neof") << std::endl;
fs << "not written";
fs.clear ();
std::cout << "G: " << (fs.eof() ? "eof" : "neof") << std::endl;
fs << i;
fs.close();
}
}
Output:
A: neof
B: neof
C: neof
D: eof
45464748
E: eof
F: eof
G: neof
File contents:
1094861636
Apparently, on Linux (Centos 7, g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)) you can seek to absurd offsets in files opened for reading. I can get the file length and check the offset myself, but shouldn't the seekg fail?
This program illustrates. No error conditions are detected, but the file is much less than 999999 bytes long.
#include <iostream>
#include <fstream>
int main(int argc, char **argv) {
std::ifstream f("./tstseek.cpp",std::ios::in);
if(!f.seekg(9999999)) {
std::cerr << "SEEK FAILED" << std::endl;
}
long int pos = f.tellg();
if(f.bad() || f.fail()) {
std::cerr << "SEEK FAILED" << std::endl;
}
if(f.eof()) {
std::cerr << "EOF AFTER SEEK" << std::endl;
}
std::string s;
std::getline(f,s);
if(f.bad() || f.fail()) {
std::cerr << "getline failed" << std::endl;
}
if(f.eof()) {
std::cerr << "EOF after getline" << std::endl;
}
std::streamsize bytesread = f.gcount();
std::cerr << "Position after seekg(9999999) = " << pos << std::endl
<< "bytes read = " << bytesread << std::endl
<< "string=[" << s << "]" << std::endl;
}
void main() {
nodLista* LS=NULL;
FILE* F=fopen("asaceva.txt","r");
if(F!=NULL) {
char buffer[100]; int id;float pret;
fscanf(F,"d",&id);
while(!feof(F)) {
fscanf(F,"f",&pret);
fscanf(F,"s",buffer);
Produs* p= creareProdus(id,pret,buffer);
LS=inserareSfarsit(LS,*p);
fscanf(F,"%d",&id);
}
afisareLista(LS);
}
_getch();
}
afisareLista: displays the list
inserareSfarsit: inserts at the end
I don't understand why it doesn't get the data from the txt file. Can you explain why?
There are a couple of issues in your code:
main not returning integer.
not using fscanf consistently and correctly with placeholders.
you are not checking the return value of fscanf for failure.
NULL should be replaced with nullptr if you have C++11 support available.
The correct code should be like this:
int main() {
nodLista* LS=NULL;
FILE* F=fopen("asaceva.txt","r");
if(F!=NULL) {
char buffer[100]; int id;float pret;
if (!fscanf(F,"%d",&id))
cout << "Error happened: " << ferror(F) << ", error string: " << strerror(errno) << endl;
while(!feof(F)) {
if (!fscanf(F,"%f",&pret))
cout << "Error happened: " << ferror(F) << ", error string: " << strerror(errno) << endl;
if (!fscanf(F,"%s",buffer))
cout << "Error happened: " << ferror(F) << ", error string: " << strerror(errno) << endl;
Produs* p= creareProdus(id,pret,buffer);
LS=inserareSfarsit(LS,*p);
if (!fscanf(F,"%d",&id))
cout << "Error happened: " << ferror(F) << ", error string: " << strerror(errno) << endl;
}
afisareLista(LS);
}
_getch();
return 0;
}
I'm trying to control the connect method timeout, but I didn't find the appropriate mean.
Just to be clear, I'm not talking about the Idle connection timeout(ConnectTimeoutOption).
The scenario I need to deal with is a database gone away, and my server has to cope with that. My current handling of things is that I'm pinging the server, and If I notice that the ping failed, I'm suspending the queries for 100 seconds. After that I'm trying to reestablish the connection. The problem is that if the database is still dead, than the connect method takes about 20 seconds to answer (can be simulated by just pulling the network cable), which is way too much for me.
This should work for you
#include <mysql++.h>
#include <cstdio>
int main()
{
mysqlpp::Connection conn;
conn.set_option(new mysqlpp::ReconnectOption(true));
conn.set_option(new mysqlpp::ConnectTimeoutOption(5));
const std::string db="mysql_cpp_data";
const std::string query_text="SELECT count(*) as total FROM stock";
conn.connect(db.c_str(), "somehost", "user", "pass");
try
{
mysqlpp::Query query=conn.query();
query << query_text;
mysqlpp::StoreQueryResult res=query.store();
std::cout << "Has " << (*res.begin())[0] << " rows\n";
}
catch(const mysqlpp::BadQuery &e)
{
std::cout << "EXCEPTION: " << e.what() << std::endl;
}
std::cout << "Make database go away now and press a key\n";
getchar();
try
{
mysqlpp::Query query=conn.query();
query << query_text;
mysqlpp::StoreQueryResult res=query.store();
std::cout << "Has " << (*res.begin())[0] << " rows\n";
}
catch(const mysqlpp::BadQuery &e)
{
std::cout << "EXCEPTION: " << e.what() << std::endl;
std::cout << "Make database come back now and press a key\n";
getchar();
while(!conn.ping())
{
sleep(1);
std::cout << "Waiting for DB to come back\n";
}
if(!conn.select_db(db))
{
std::cout << "Failed to change DB\n";
}
}
try
{
mysqlpp::Query query=conn.query();
query=conn.query();
query << query_text;
mysqlpp::StoreQueryResult res=query.store();
std::cout << "Has " << (*res.begin())[0] << " rows\n";
}
catch(const mysqlpp::BadQuery &e)
{
std::cout << "EXCEPTION: " << e.what() << " " << e.errnum() << std::endl;
}
return 0;
}
try this out.
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmdline.h>
#include <mysql++.h>
#define DBS "library"
#define USR "root"
#define PAS "rootsman"
using namespace std;
using namespace mysqlpp;
int main(int argc, char *argv[]) {
//for true cgi but in this case it works, kind of baffling!
mysqlpp::examples::CommandLine cmdline(argc, argv, USR, PAS);
if (!cmdline) return 1;
mysqlpp::Connection conn(true);
conn.set_option(new mysqlpp::ReconnectOption(true));
conn.set_option(new mysqlpp::ConnectTimeoutOption(5));
conn.connect(DBS, cmdline.server(), cmdline.user(), cmdline.pass());
try {
mysqlpp::String sql("select firstname from person");
mysqlpp::Query query = conn.query(sql);
mysqlpp::StoreQueryResult res = query.store();
mysqlpp::StoreQueryResult::const_iterator it;
int count = 1;
for (it = res.begin(); it != res.end(); ++it) {
mysqlpp::Row row = *it;
cout << count << "\t" << row[0] << endl;
++count;
}
} catch (const mysqlpp::BadQuery& bq) {
cerr << "query error: " << bq.what() << endl;
return -1;
}
cout << "\nmake database fly away now by pressing a key>" << endl;
getchar();
try {
mysqlpp::Query query = conn.query();
mysqlpp::String sql("select count(*) as total from person");
query << sql;
mysqlpp::StoreQueryResult res = query.store();
cout << "has " << (*res.begin())[0] << " rows" << endl;
} catch (mysqlpp::BadQuery& e) {
cerr << "\n bad query 2>" << e.what() << endl;
cout << "\nmake database fly back now by pressing enter>" << endl;
while (!conn.ping()) {
//sleep(1);
cout << "\nwaiting for database to fly back>" << endl;
}
if (!conn.select_db(DBS)) {
cerr << "\nfailed to reconnect" << endl;
}
}
try {
mysqlpp::Query query = conn.query();
//this is how my relation and its attributes looks like
String sql("insert into person(firstname,lastname,gender,love,angry,"
"forgiving) values('joy57/qxx','crimson','male','high','medium','high');");
query << sql;
query.execute();
cerr << "\n **inserted successfully**\n" << endl;
} catch (mysqlpp::BadQuery& e) {
cerr << "bad query 3>" << e.what() << e.errnum() << endl;
return -1;
}
cerr << "Jesus helps me when i stumble and fall" << endl;
return 0;
}