getline has a strange behaviour in reading a text file - c++

I have a strange behaviour with a piece of code I have wrote. There is the code :
#include "pugixml.hpp"
#include <cassert>
#include <string>
#include <iostream>
#include <fstream>
#include <optional>
namespace xml = pugi;
void read_ascii_file(const std::string& filename)
{
std::ifstream file(filename, std::ios::in);
if(!file) {
std::cerr << "[ERREUR - read_ascii_file] Impossible d'ouvrir le fichier " << filename << "! Vérifier son existence." << std::endl;
abort();
}
std::string tmp;
while(std::getline(file, tmp))
{
//Do nothing here
}
file.close();
}
class Foo{
public:
Foo(const xml::xml_document& doc)
{
_base_node = doc.child("test");
std::string id = _base_node.child("data1").child_value("id");
std::cout << "id from constructor " << id <<std::endl;
}
void bar()
{
std::string id = _base_node.child("data2").child_value("id");
std::cout << "id from bar " << id <<std::endl;
}
private:
xml::xml_node _base_node;
};
std::optional<Foo> make(const std::string& filename)
{
xml::xml_document doc;
xml::xml_parse_result result = doc.load_file(filename.c_str());
if(result.status != xml::xml_parse_status::status_ok)
return {};
else
return Foo(doc);
}
int main()
{
std::string filename = "xml_test.dat";
std::optional<Foo> f = make(filename);
if(!f)
std::abort();
else
{
std::string filename = "lbl-maj_for_test.dat";
//read_ascii_file(filename);
f->bar();
}
return 0;
}
The file xml_test.dat is :
<test>
<data1>
<id>1</id>
</data1>
<data2>
<id>2</id>
</data2>
</test>
This code giving an output :
id from constructor 1
id from bar 2
But when I uncomment the line //read_ascii_file(filename);, the output become :
id from constructor 1
Erreur de segmentation
gdb give me the error :
#0 0x00007ffff7f84b20 in pugi::xml_node::child(char const*) const () from /lib/x86_64-linux-gnu/libpugixml.so.1
#1 0x00005555555578ba in Foo::bar (this=0x7fffffffdf40) at /home/guillaume/dev/C++/projects/moteur_de_calcul/test/test_xml_node.cpp:42
#2 0x00005555555575ec in main () at /home/guillaume/dev/C++/projects/moteur_de_calcul/test/test_xml_node.cpp:73
The file lbl-maj_for_test.dat is a txt file of 132 lines and none seems to have a length more than 50 characters. I think to a an encoding problem, but I have no clue how I can resolve this problem ...

This has nothing to do with getline. Uncommenting/commenting things when your program has undefined behaviour can lead to red herrings such as this.
The problem is that your nodes are all dangling, since you do not persist doc. By the time you call bar(), _base_node is dead/invalid/orphaned.
From the documentation:
xml_document is the owner of the entire document structure; destroying the document destroys the whole tree.
Assuming the library supports it, I'd move doc into Foo and store it as a member by value.

Related

creating a class vector that does not delete it's content

I am a beginner , so i wanted to ask , can we create a class object vector/array , that does not delete it's content when i close the program like , so like I want a customer record , but whenever if we try to restart the program we need to enter the customer details again and again ...
how to prevent that from happening
#include <iostream>
#include <vector>
using namespace std;
class customer{
public:
int balance;
string name;
int password;
};
int main(){
vector <customer> cus;
...
if(choice == 1){
cout << cus[i].balance
}
return 0;
}
As a complement to Adam's answer, it is possible to encapsulate the serialization in the container class itself. Here is an simplified example:
The header file defining a persistent_vector class that saves its content to a file:
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <initializer_list>
namespace {
// Utility functions able to store one element of a trivially copyable type
template <class T>
std::ostream& store1(std::ostream& out, const T& val) {
out.write(reinterpret_cast<const char*>(&val), sizeof(val));
return out;
}
template <class T>
std::istream& load1(std::istream& in, T& val) {
in.read(reinterpret_cast<char*>(&val), sizeof(val));
return in;
}
// Specialization for the std::string type
template <>
std::ostream& store1<std::string>(std::ostream& out, const std::string& val) {
store1<size_t>(out, val.size());
if (out) out.write(val.data(), val.size());
return out;
}
template <>
std::istream& load1<std::string>(std::istream& in, std::string& val) {
size_t len;
load1<size_t>(in, len);
if (in) {
char* data = new char[len];
in.read(data, len);
if (in) val.assign(data, len);
delete[] data;
}
return in;
}
}
template <class T>
class persistent_vector {
const std::string path;
std::vector<T> vec;
// load the vector from a file
void load() {
std::ifstream in(path);
if (in) {
for (;;) {
T elt;
load1(in, elt);
if (!in) break;
vec.push_back(elt);
}
if (!in.eof()) {
throw std::istream::failure("Read error");
}
in.close();
}
}
// store the vector to a file
void store() {
std::ofstream out(path);
size_t n = 0;
if (out) {
for (const T& elt : vec) {
store1(out, elt);
if (!out) break;
++n;
}
}
if (!out) {
std::cerr << "Write error after " << n << " elements on " << vec.size() << '\n';
}
}
public:
// a bunch of constructors, first ones load data from the file
persistent_vector(const std::string& path) : path(path) {
load();
}
persistent_vector(const std::string& path, size_t sz) :
path(path), vec(sz) {
load();
};
// last 2 constructors ignore the file because they do receive data
persistent_vector(const std::string& path, size_t sz, const T& val) :
path(path), vec(sz, val) {
};
persistent_vector(const std::string& path, std::initializer_list<T> ini) :
path(path), vec(ini) {
}
// destructor strores the data to the file before actually destroying it
~persistent_vector() {
store();
}
// direct access to the vector (const and non const versions)
std::vector<T>& data() {
return vec;
}
const std::vector<T>& data() const {
return vec;
}
};
It can, out of the box, handle any trivially copyable type and std::string. User has to provide specializations of store1 and load1 for custom types.
Here is a trivial program using it:
#include <iostream>
#include <string>
#include "persistent_vector.h"
int main() {
std::cout << "Create new vector (0) or read an existing one (1): ";
int cr;
std::cin >> cr;
if (!std::cin || (cr != 0 && cr != 1)) {
std::cout << "Incorrect input\n";
return 1;
}
if (cr == 0) {
persistent_vector<std::string> v("foo.data", 0, "");
// skip to the end of line...
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
for (;;) {
std::string line;
std::cout << "Enter a string to add to the vector (empty string to end program)\n";
std::getline(std::cin, line);
if (line.empty()) break;
v.data().push_back(line);
}
}
else {
persistent_vector<std::string> v("foo.data");
for (const std::string& i : v.data()) {
std::cout << i << '\n';
}
}
return 0;
}
When a programmer creates a vector class, he must ensure that the resources acquired for that vector are released when they are no longer needed. (See RAII)
C++ Reference : https://en.cppreference.com/w/cpp/language/raii
Wikipedia : https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
Stack Overflow : What is meant by Resource Acquisition is Initialization (RAII)?
Microsoft : https://learn.microsoft.com/en-us/cpp/cpp/object-lifetime-and-resource-management-modern-cpp?view=msvc-170
Before the program closes, all resources must be released.
(No leaking resources, memory included)
It is not possible to create a vector class that does not delete its contents after closing a program. Secure operating systems will release program resources when the program is closed.
If you want the program not to lose customer information after closing, you need to save the information in persistent (non-volatile) storage device, such as a disk.
As CinCout, 김선달, Serge Ballesta say, you have to save the customer information to a file, and write the program so that you can read that file during the start of the program.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
struct customer {
std::string name;
int balance;
int password;
};
int main() {
std::vector <customer> customers;
std::ifstream ifs("info.txt");
{
customer customer{};
while (ifs >> customer.name >> customer.balance >> customer.password)
customers.push_back(customer);
}
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
std::cout << "\n\nWelcome\n\n";
std::ofstream ofs("info.txt", std::ios_base::app);
char cont{};
do {
customer customer{};
std::cout << "Name : ";
std::cin >> customer.name;
std::cout << "Balance : ";
std::cin >> customer.balance;
std::cout << "Password : ";
std::cin >> customer.password;
ofs << customer.name << ' ' << customer.balance << ' ' << customer.password << '\n';
std::cout << "Add another customer? (Y/N) : ";
std::cin >> cont;
} while (cont == 'Y');
for (const auto& [name, balance, password] : customers) {
std::cout <<
"\nName : " << name <<
"\nBalance : " << balance <<
"\nPassword : " << password <<
'\n';
}
}
CPlusPlus : https://www.cplusplus.com/doc/tutorial/files/
LearnCpp : https://www.learncpp.com/cpp-tutorial/basic-file-io/
(About File I/O)
This program is a prototype, I left some things incomplete (like check readings, user-defined I/O operators, duplicate code, formatting, reallocations of customers, ifs is not required after range-for + structured binding,...).
I suggest you read the book "Programming: Principles and Practice Using C+", I’m reading it and it helped me a lot.
(I’m also a beginner)
Edit: I also suggest you use "using namespace std;" only for small projects, examples or simple exercises.
Do not use "using namespace std;" for real projects, large projects or projects that may include other dependencies because the use of "using namespace std;" could lead to a possible naming collisions between names within std and the names of other codes and libraries.
It’s not good practice to use it all the time.

String is not being directed to Output file (C++)

So I'm trying to direct the String that I get from a function to an output file. What's happening is that the buildTree function is creating a binary tree of vector strings and then after it has finished, it calls the printInorder function to print the function in order. It will correctly print out the tree if I replace file << tree.printVector(tempRoot->data); with cout << tree.printVector(tempRoot->data); But trying to direct the returned string from tree.printVector() doesn't seem to do anything. The file.txt is still blank after run. Thanks for the help
here's the code
#include <iostream>
#include "node.h"
#include "tree.h"
#include "cleanString.h"
#include <string>
#include <fstream>
using std::cout;
using std::endl;
BST tree, *root = nullptr;
void buildTree(std::string name){
ifstream file;
file.open(name);
if (file){
std::string word;
file >> word;
word = cleanString(word);
root = tree.Insert(root, word);
while (file >> word){
word = cleanString(word);
tree.Insert(root, word);
}
//tree.Inorder(root);
printInorder(root);
} else{
cout << "not a file" << endl;
}
file.close();
}
void printInorder(BST* tempRoot){
ofstream file;
std::string vecStrings;
file.open("file.txt");
if (!tempRoot) {
return;
}
printInorder(tempRoot->left);
vecStrings = tree.printVector(tempRoot->data);
file << vecStrings << endl; // the issue here: wont put string in file.txt
cout << vecStrings << endl; // but this will print
printInorder(tempRoot->right);
file.close();
}
void printPreorder(){
}
void printPostorder(){
}
Rather than a method that opens and closes the file, how about writing:
std::ostream &operator<<(std::ostream &str, const BST &) {
...
}
Then you have a generic print method that you can send to any output stream.

Why is my function returning an empty string?

I was wondering if someone could please illuminate me on why the below code does not behave the way I expect. By that I mean I expect the line
std::cout << myEngine.getDesc() << std::endl;
to print out: "Desc1"
But I get an empty string? I figured that maybe it was because I was splitting up my rudimentary code into different files incorrectly but I get the same thing when I put all the code into one file.
StringErrorTest.cpp
#include <iostream>
#include "Engine.h"
int main()
{
std::cout << "Compiling & Running!";
Engine myEngine;
std::string t1 = "Hello ";
std::cout << myEngine.getDesc() << std::endl;
std::cout << t1 << std::endl;
return 0;
}
Engine.h
#include <string>
class Engine {
private:
std::string m_Description;
std::string m_Description2;
public:
Engine();
std::string getDesc();
void setDesc(std::string desc);
std::string getDesc2();
void setDesc2(std::string desc2);
std::string spitItOut();
};
Engine.cpp
#include "Engine.h"
Engine::Engine()
{
std::string m_Description = "Desc1";
std::string m_Description2 = "Desc2";
}
std::string Engine::getDesc()
{
return m_Description;
}
std::string Engine::getDesc2()
{
return m_Description2;
}
By the way I did search for similar questions but they were all a bit more complex than mine. I feel I have a very basic misunderstanding going on here.
In Engine::Engine(), you're creating two local objects named m_Description and m_Description2, which have nothing to do with the data members with same names; they hide the names of data members.
What you want to is to assign them as
Engine::Engine()
{
m_Description = "Desc1"; // or this->m_Description = "Desc1"
m_Description2 = "Desc2"; // or this->m_Description2 = "Desc2"
}
Or initialize them as
Engine::Engine() : m_Description("Desc1"), m_Description2("Desc2")
{
}

How can I determine the current size of the file opened by std::ofstream?

I have a class that has a filestream of type ofstream. The constructor opens the file in append mode and all the messages always get written at the end of the file.
I need to write into outputFile up to some fixed size say 1Mb, then I need to close, rename, and compress it, and then open a new file of the same name.
This needs to be done when a certain size of file is reached.
I tried using tellg() but after reading stuffs (and this) on internet, I understood that this is not the right approach.
As I'm new to C++, I'm trying to find out the most optimized and correct way to get the accurate current size of file opened by ofstream?
class Logger {
std::ofstream outputFile;
int curr_size;
Logger (const std::string logfile) : outputFile(FILENAME,
std::ios::app)
{
curr_size = 0;
}
};
Somewhere in the program, I'm writing data into it:
// ??? Determine the size of current file ???
if (curr_size >= MAX_FILE_SIZE) {
outputFile.close();
//Code to rename and compress file
// ...
outputFile.open(FILENAME, std::ios::app);
curr_size = 0;
}
outputFile << message << std::endl;
outputFile.flush();
fstreams can be both input and output streams. tellg() will return the input position and tellp() will tell you of the output position. tellp() will after appending to a file tell you its size.
Consider initializing your Logger like this (edit: added example for output stream operator):
#include <iostream>
#include <fstream>
class Logger {
std::string m_filename;
std::ofstream m_os;
std::ofstream::pos_type m_curr_size;
std::ofstream::pos_type m_max_size;
public:
Logger(const std::string& logfile, std::ofstream::pos_type max_size) :
m_filename(logfile),
m_os(m_filename, std::ios::app),
m_curr_size(m_os.tellp()),
m_max_size(max_size)
{}
template<typename T>
friend Logger& operator<<(Logger&, const T&);
};
template<typename T>
Logger& operator<<(Logger& log, const T& msg) {
log.m_curr_size = (log.m_os << msg << std::flush).tellp();
if(log.m_curr_size>log.m_max_size) {
log.m_os.close();
//rename & compress
log.m_os = std::ofstream(log.m_filename, std::ios::app);
log.m_curr_size = log.m_os.tellp();
}
return log;
}
int main()
{
Logger test("log", 4LL*1024*1024*1024*1024);
test << "hello " << 10 << "\n";
return 0;
}
If you use C++17 or have an experimental version of <filesystem> available, you could also use that to get the absolute file size, like this:
#include <iostream>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
class Logger {
fs::directory_entry m_logfile;
std::ofstream m_os;
std::uintmax_t m_max_size;
void rotate_if_needed() {
if(max_size_reached()) {
m_os.close();
//rename & compress
m_os = std::ofstream(m_logfile.path(), std::ios::app);
}
}
public:
Logger(const std::string& logfile, std::uintmax_t max_size) :
m_logfile(logfile),
m_os(m_logfile.path(), std::ios::app),
m_max_size(max_size)
{
// make sure the path is absolute in case the process
// have changed current directory when we need to rotate the log
if(m_logfile.path().is_relative())
m_logfile = fs::directory_entry(fs::absolute(m_logfile.path()));
}
std::uintmax_t size() const { return m_logfile.file_size(); }
bool max_size_reached() const { return size()>m_max_size; }
template<typename T>
friend Logger& operator<<(Logger&, const T&);
};
template<typename T>
Logger& operator<<(Logger& log, const T& msg) {
log.m_os << msg << std::flush;
log.rotate_if_needed();
return log;
}
int main()
{
Logger test("log", 4LL*1024*1024*1024*1024);
std::cout << test.size() << "\n";
test << "hello " << 10 << "\n";
std::cout << test.size() << "\n";
test << "some more " << 3.14159 << "\n";
std::cout << test.size() << "\n";
return 0;
}
I gave it a try with tellp() and it works fine for me:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream myFile("data.txt", ios_base::app);
myFile << "Hello World!" << endl;
cout << myFile.tellp() << endl;
return 0;
}
This is the output, when calling this program:
$ ./program
13
$ ./program
26
$ ./program
39

Boost deserialisation issue : Input Stream Error at runtime (c++)

I've serialised a map fine
std::map<std::string, std::string> userandPass;
saveData< std::map<std::string, std::string> >("userandPassBackup.txt", userandPass); // have removed &. why is this needed. i want to pass by reference
using the function
template <typename SaveClass>
void saveData(const std::string filename, SaveClass& c)
{
// File to be written to
boost::filesystem::remove(boost::filesystem::current_path() /
filename);
boost::filesystem::path myFile = boost::filesystem::current_path() /
filename;
// Declare an output file stream ofs that writes serialises to our
//backup file.
boost::filesystem::ofstream ofs(myFile.native());
// Declare archive ta that will use our output file stream
boost::archive::text_oarchive ta(ofs);
// Write to file
ta << c;
// How many records have been archived?
std::cout << c.size() << " records from have been successfully backed
up to " << myFile << "\n";
}
Deserialising (loading) however, fails, using:
loadData< std::map<std::string, std::string> >("userandPassBackup.txt", userandPass);
where the function is:
template <typename LoadClass>
void loadData(const std::string filename, LoadClass& c)
{
// File to be written to
boost::filesystem::remove(boost::filesystem::current_path() /
filename);
boost::filesystem::path myFile = boost::filesystem::current_path() /
filename;
// Declare an output file stream ofs that writes serialises to our
//backup file.
boost::filesystem::ifstream ifs(myFile.native());
// Declare archive ta that will use our output file stream
boost::archive::text_iarchive ta(ifs);
// Write to file
ta >> c;
// How many records have been archived?
std::cout << c.size() << " records from have been successfully backed
up to " << myFile << "\n";
}
My project compiles, but when I run it, I get the following error concerning the input stream:
libc++abi.dylib: terminating with uncaught exception of type boost::archive::archive_exception: input stream error
Abort trap: 6
I don't see why this is happening. Would anyone be so kind as to point me in the right direction?
Thanks
It seems like you copypasted loadData body from saveData. You delete file that you are trying to load as a first step by calling boost::filesystem::remove.
#VTT got the biggest bug.
Here's my free code review:
you don't need boost::filesystem to std::remove(filename)
instead of currentdir / filename you should do boost::filesystem::make_absolute
that is already default behaviour
you should not use native() since you can pass the path
the argument to saveData can be const&
Do not explicitly pass template arguments: function calls do template argument deduction.
Never mind that your comments are very redundant.
Live On Coliru
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/map.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <cstdio>
#include <iostream>
#include <fstream>
template <typename SaveClass>
void saveData(const std::string filename, SaveClass const& c)
{
std::remove(filename.c_str());
std::ofstream ofs(filename);
boost::archive::text_oarchive ta(ofs);
ta << c;
}
template <typename LoadClass>
void loadData(const std::string filename, LoadClass& c)
{
std::ifstream ifs(filename);
boost::archive::text_iarchive ta(ifs);
ta >> c;
}
int main() {
std::string filename = "userandPassBackup.txt";
{
std::map<std::string, std::string> userandPass {
{ "John", "pa$$w0rd" },
{ "Jane", "welcome01" } };
saveData(filename, userandPass);
std::cout << userandPass.size() << " records from have been successfully backed up to " << filename << "\n";
}
{
std::map<std::string, std::string> userandPass;
loadData(filename, userandPass);
std::cout << userandPass.size() << " records from have been successfully restored from " << filename << "\n";
}
}
Prints
2 records from have been successfully backed up to userandPassBackup.txt
2 records from have been successfully restored from userandPassBackup.txt