I have an issue with pushing back object to a list of objects.
std::ostream& operator << (std::ostream& os, const Class1& sk) {
os << sk.X << 'x' << sk.Y << 'x' << sk.H << ';' << sk.a << ';' << sk.b << '\n';
return os;
}
std::istream& operator >> (std::istream& is, Class1& sk) {
char ch;
is >> sk.X >> ch >> sk.Y >> ch >> sk.H >> ch >> sk.a >> ch >> sk.b >> ch;
return is;
}
void Class1::GetSK_list(std::list<Class1>& SK_list) {
std::ifstream file("file.txt", std::ios::in);
std::list<Class1>::iterator iter = SK_list.begin();
while(file >> *iter) {
"std::cout << *iter"; // checking
SK_list.push_back(*iter);
iter++;
}
file.close();
}
void Class1::SaveSK_list(std::list<Class1>& SK_list) {
std::ofstream file("file.txt", std::ios::out);
for(std::list<Class1>::iterator iter = SK_list.begin(); iter != SK_list.end(); iter++)
file << *iter;
file.close();
}
When I'm using SaveSK_list function, it works correctly.
The problem is with GetSK_list function. If there's one line in file (e.g. 1x2x3;4;5) file >> *iter doesn't work. There's nothing printed (I believe std::cout << *iter is never reached).
If there's more than one line in file, only the very first one is being printed. After that std::bad_alloc error appears.
Btw. these functions are in Class1 in order to use operators overloading.
You can use an insert iterator, which will automatically call push_back() on the list:
void GetSK_list(std::list<Class1>& SK_list) {
auto iter = std::back_inserter(SK_list);
std::ifstream file("file.txt", std::ios::in);
Class1 tmp;
while(file >> tmp) {
*iter++ = tmp;
}
file.close();
}
You can do even better by combining with an istream_iterator and the copy algorithm:
void GetSK_list(std::list<Class1>& SK_list) {
std::ifstream file("file.txt", std::ios::in);
std::copy(std::istream_iterator<Class1>(file),
std::istream_iterator<Class1>(),
std::back_inserter(SK_list));
}
There's also no need to call file.close(); the file is closed in the ifstream destructor.
Related
I am trying to read a file of the following format
id1 1 2 3
id2 2 4 6
id3 5 6 7
...
using this code
Dataset::Dataset(ifstream &file) {
string token;
int i = 0;
while (!file.eof() && (file >> token)){
// read line tokens one-by-one
string ID = token;
vector<int> coords;
while ((file.peek()!='\n') && (!file.eof()) && (file >> token)) {
coords.push_back(atoi(token.c_str()));
}
points.push_back(new Point(ID, coords));
i++;
}
cout << "Loaded " << i << " points." << endl;
}
But it tells me I have read 0 points. What am I doing wrong?
Edit: I am openning this using input_stream.open(input_file) and file.good() returns true.
Edit #2: actually .good() returns true the first time and then false. What is that all about?
Edit #3: GUYS. IT'S FREAKING WINDOWS. When i put the path as Dataset/test.txt by cin it works and when I do it like Dataset\test.txt by the commandline it doesn't...
Now the problem is it seems not stop at new lines!
Edit #4: Freaking windows again! It was peeking '\r' instead of '\n'.
Here's an idea: overload operator>>:
struct Point
{
int x, y, z;
friend std::istream& operator>>(std::istream& input, Point& p);
};
std::istream& operator>>(std::istream& input, Point& p)
{
input >> p.x;
input >> p.y;
input >> p.z;
input.ignore(10000, '\n'); // eat chars until end of line.
return input;
}
struct Point_With_ID
: public Point
{
std::string id;
friend std::istream& operator>>(std::istream& input, Point_With_ID& p);
};
std::istream& operator>>(std::istream& input, Point_With_ID& p)
{
input >> p.id;
input >> static_cast<Point&>(p); // Read in the parent items.
return input;
}
Your input could look like this:
std::vector<Point_With_ID> database;
Point_With_ID p;
while (file >> p)
{
database.push_back(p);
}
I separated the Point class so that it can be used in other programs or assignments.
I managed to make it work by accounting for both '\r' and '\n' endings and ignoring trailing whitespace like this:
Dataset::Dataset(ifstream &file) {
string token;
int i = 0;
while (file >> token){
// read line tokens one-by-one
string ID = token;
vector<int> coords;
while ((file.peek()!='\n' && file.peek()!='\r') && (file >> token)) { // '\r' for windows, '\n' for unix
coords.push_back(atoi(token.c_str()));
if (file.peek() == '\t' || file.peek() == ' ') { // ignore these
file.ignore(1);
}
}
Point p(ID, coords);
points.emplace_back(p);
i++;
// ignore anything until '\n'
file.ignore(32, '\n');
}
cout << "Loaded " << i << " points." << endl;
}
Probably not the best of the solutions suggested but it's working!
You should not use eof() in a loop condition. See Why is iostream::eof inside a loop condition considered wrong? for details. You can instead use the following program to read into the vector of Point*.
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
class Point
{
public:
std::string ID = 0;
std::vector<int> coords;
Point(std::string id, std::vector<int> coord): ID(id), coords(coord)
{
}
};
int main()
{
std::vector<Point*> points;
std::ifstream file("input.txt");
std::string line;
int var = 0;
while (std::getline(file, line, '\n'))//read line by line
{
int j = 0;
std::istringstream ss(line);
std::string ID;
ss >> ID;
std::vector<int> coords(3);//create vector of size 3 since we already know only 3 elements needed
while (ss >> var) {
coords.at(j) = var;
++j;
}
points.push_back(new Point(ID, coords));
}
std::cout<<points.size()<<std::endl;
//...also don't forget to free the memory using `delete` or use smart pointer instead
return 0;
}
The output of the above program can be seen here.
Note that if you're using new then you must use delete to free the memory that you've allocated. This was not done in the above program that i have given since i only wanted to show how you can read the data in your desired manner.
You've baked everything up in a complex deserializing constructor. This makes the code hard to understand and maintain.
You have a coordinate, so make class for that, we can call it Coord, that is capable of doing its own deserializing.
You have a Point, which consists of an ID and a coordinate, so make a class for that, that is capable of doing its own deserializing.
The Dataset will then just use the deserializing functions of the Point.
Don't limit deserializing to ifstreams. Make it work with any istream.
Deserializing is often done by overloading operator>> and operator<< for the types involved. Here's one way of splitting the problem up in smaller parts that are easier to understand:
struct Coord {
std::vector<int> data;
// read one Coord
friend std::istream& operator>>(std::istream& is, Coord& c) {
if(std::string line; std::getline(is, line)) { // read until end of line
c.data.clear();
std::istringstream iss(line); // put it in an istringstream
// ... and extract the values:
for(int tmp; iss >> tmp;) c.data.push_back(tmp);
}
return is;
}
// write one Coord
friend std::ostream& operator<<(std::ostream& os, const Coord& c) {
if(not c.data.empty()) {
auto it = c.data.begin();
os << *it;
for(++it; it != c.data.end(); ++it) os << ' ' << *it;
}
return os;
}
};
struct Point {
std::string ID;
Coord coord;
// read one Point
friend std::istream& operator>>(std::istream& is, Point& p) {
return is >> p.ID >> p.coord;
}
// write one Point
friend std::ostream& operator<<(std::ostream& os, const Point& p) {
return os << p.ID << ' ' << p.coord;
}
};
struct Dataset {
std::vector<Point> points;
// read one Dataset
friend std::istream& operator>>(std::istream& is, Dataset& ds) {
ds.points.clear();
for(Point tmp; is >> tmp;) ds.points.push_back(std::move(tmp));
if(!ds.points.empty()) is.clear();
return is;
}
// write one Dataset
friend std::ostream& operator<<(std::ostream& os, const Dataset& ds) {
for(auto& p : ds.points) os << p << '\n';
return os;
}
};
If you really want a deserializing constructor in Dataset you just need to add these:
Dataset() = default;
Dataset(std::istream& is) {
if(!(is >> *this))
throw std::runtime_error("Failed reading Dataset");
}
You can then open your file and use operator>> to fill the Dataset and operator<< to print the Dataset on screen - or to another file if you wish.
int main() {
if(std::ifstream file("datafile.dat"); file) {
if(Dataset ds; file >> ds) { // populate the Dataset
std::cout << ds; // print the result to screen
}
}
}
Demo
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
The code is supposed to open an existing text file, transfer the contents into the array, then create a new text file and then write the array contents into the new text file. The problem I'm having is that the code only outputs the last line of the content from the new text file.
file.open("Patient.txt", ios::in);
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB >> arr[i].address >> arr[i].Dr_name >> arr[i].V_date;
/*cout << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date << endl;*/
}
file.close();
files.open("Patients_2.txt");
if (files.is_open()) {
for (i; i < 30; i++) {
files << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date;
}
}
files.close();
patientss.open("Patients_2.txt");
cout << "Patient 2: " << endl;
while (!patientss.eof()) {
getline(patientss, line);
cout << line << endl;
}
patientss.close();
system("pause");
return 0;
}
IMHO, you should overload the formatted insertion and extraction operators in your patient class:
struct Patient
{
std::string name;
std::string dob;
std::string address;
std::string dr_name;
std::string v_date;
friend std::istream& operator>>(std::istream& input, Patient& p);
friend std::ostream& operator<<(std::ostream& output, const Patient& p);
};
std::istream& operator>>(std::istream& input, Patient& p)
{
std::getline(input, p.name);
std::getline(input, p.dob);
std::getline(input, p.address);
std::getline(input, p.dr_name);
std::getline(input, p.v_date);
return input;
}
std::ostream& operator<<(std::ostream& output, const Patient& p)
{
output << p.name << "\n";
output << p.dob << "\n";
output << p.address << "\n";
output << p.dr_name << "\n";
output << p.v_date << "\n";
return output;
}
The above makes input and output easier:
std::vector<Patient> database;
Patient p;
while (input_file >> p)
{
database.push_back(p);
}
const unsigned int quantity = database.size();
for (unsigned int i = 0; i < quantity; ++quantity)
{
output_file << database[i];
}
The above code also supports the concepts of encapsulation and data hiding. The Patient struct is in charge or reading its members because it knows the data types of the members. The code external to the Patient is only concerned with the input and output of a Patient instance (doesn't care about the internals).
This loop has a few problems:
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB ....
You never increase i so the same arr[i] will be overwritten time and time again.
You use !file.eof() as a condition to stop reading. eof() does not get set until after you've tried to read beyond the end of the file, which means that if you had increased i as you should, the last arr would be empty / broken. Instead check if the extraction from the stream succeeded. Since the stream is returned when you do stream >> var and has an overload for explicit operator bool() const which returns !fail() you can do this check directly in your loop:
while(stream >> var) { extraction success }
Using formatted input (>>) for string fields that are likely to contain spaces is however not a good idea. Your name, nung khual, would be split so nung would go into name and khual would go into DOB. It's better to use a field separator that is very unlikely to be included in anyone's name. \n is usually good and works well with std::getline.
std::getline returns the stream that you gave as an argument which means that you can chain getlines similarly to stream >> var1 >> var2, except it's a little more verbose.
getline(getline(stream, var1), var2) will put the first line in var1 and the second line in var2.
To make input and output a little simpler you can add stream operators for your data type and make the input stream operator use getline for your fields.
Example:
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
struct data_t {
std::string name;
std::string DOB;
std::string address;
std::string Dr_name;
std::string V_date;
};
// input stream operator using chained getlines
std::istream& operator>>(std::istream& is, data_t& d) {
using std::getline;
return getline(getline(getline(getline(getline(is,
d.name), d.DOB), d.address), d.Dr_name), d.V_date);
}
// output stream operator
std::ostream& operator<<(std::ostream& os, const data_t& d) {
return os << d.name << '\n'
<< d.DOB << '\n'
<< d.address << '\n'
<< d.Dr_name << '\n'
<< d.V_date << '\n';
}
int main() {
std::vector<data_t> arr;
if(std::ifstream file("Patient.txt"); file) {
data_t tmp;
while(file >> tmp) { // remember, no eof() needed
arr.push_back(tmp);
}
}
if(std::ofstream file("Patients_2.txt"); file) {
for(const data_t& d : arr) {
file << d;
}
}
if(std::ifstream patientss("Patients_2.txt"); patientss) {
data_t tmp;
while(patientss >> tmp) {
std::cout << tmp;
}
}
}
I want to parse a string of numbers into a vector of elements. The string consists of blocks of four numbers, separated by ( ) : /, and each block is separated by a ;.
Specifically, the string is in this format: int(int):float/float;, see code sample below. I think I could use a regular expression, but since the data is so structured, I'm sure there must be a more approachable and easier way to parse such a string. I'm using istringstream, but it feels a bit clumsy.
std::string line = "0(0):0/0;1(2):0.01/0.02;2(4):0.02/0.04;3(6):0.03/0.06;"
struct Element {
int a;
int b;
int c;
int d;
};
std::vector<Element> = parse(line);
std::vector<Element> parse(std::string line)
{
std::vector<Element> elements;
std::istringstream iss(line);
while(iss) {
char dummy;
Element element;
iss >> element.a;
iss.read(&dummy,sizeof(dummy)); // (
iss >> element.b;
iss.read(&dummy,sizeof(dummy)); // )
iss.read(&dummy,sizeof(dummy)); // :
iss >> element.c;
iss.read(&dummy,sizeof(dummy)); // /
iss >> element.d;
iss.read(&dummy,sizeof(dummy)); // ;
if (!iss) {break;}
elements.push_back(element);
}
return elements;
}
My questions:
What would be a good way to parse? Should I use std::stringstream and read in number by number and 'chop off' the characters in between? As done in the code sample?
This code has a bug and attempts to read one extra set of values, because while(iss) is still true, after the last character has been read in. How to terminate this loop without testing after each iss>>? Or more generally, how to loop over extractions from istringstream?
Your data are well structured, you can easily overload operator>> to extract the class members from an std::ifstream and then keep reading them from an istringstream or a file stream.
Here is a possible implementation:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iterator>
#include <stdexcept>
class Element
{
public:
Element() {}
Element(int aa, int bb, float cc, float dd) : a{aa}, b{bb}, c{cc}, d{dd} {}
friend std::istream &operator>> (std::istream &in, Element &e);
friend std::ostream &operator<< (std::ostream &out, Element const &e);
private:
int a;
int b;
float c;
float d;
};
std::istream &operator>> (std::istream &in, Element &e)
{
char delimiter;
if ( not ( in >> e.a >> delimiter and delimiter == '(' and
in >> e.b >> delimiter and delimiter == ')' and
in >> delimiter and delimiter == ':' and
in >> e.c >> delimiter and delimiter == '/' and
in >> e.d >> delimiter and delimiter == ';' )
and not in.eof() )
{
in.setstate(std::ios_base::failbit);
}
return in;
}
std::ostream &operator<< (std::ostream &out, Element const &e)
{
return out << e.a << '(' << e.b << "):" << e.c << '/' << e.d << ';';
}
std::vector<Element> read_Elements_from(std::istream &in)
{
std::vector<Element> tmp (
std::istream_iterator<Element>{in},
std::istream_iterator<Element>{}
);
if ( not in.eof() )
throw std::runtime_error("Wrong format");
return tmp;
}
int main()
{
try
{
using std::cout;
std::istringstream iss {
"0(0):0/0;1(2):0.01/0.2;2(4):0.02/0.04;3(6):0.03/0.06;"
};
auto els_s = read_Elements_from(iss);
cout << "Elements read from the string:\n";
for ( auto const &i : els_s )
{
cout << i << '\n';
}
// assuming a file which lines are similar to the string provided
std::ifstream input_file {"input_data.txt"};
if ( not input_file )
throw std::runtime_error("Can't open input file");
auto els_f = read_Elements_from(input_file);
cout << "\nElements read from the file:\n";
for ( auto const &i : els_f )
{
cout << i << '\n';
}
}
catch ( std::exception const &e )
{
std::cerr << "\nAn unexpected problem cause this application to end:\n\n"
<< e.what() << ".\n\n";
return EXIT_FAILURE;
}
}
I'm having an issue with istringstream not storing the values it reads. Here is what I have:
if(inputFile.good()){ //Make sure file is open before trying to work with it
//Begin Working with information
cout << "\tIn File: " << input << endl;
cout << "------------------------------------" << endl;
int number_of_lines = 0;
std::string line;
while (std::getline(inputFile, line)){
++number_of_lines;
}
Time times[number_of_lines];
double math[number_of_lines];
std::string input;
int hh, mm;
for(int loop=0;loop<number_of_lines;loop++){
std::getline(inputFile, input);
std::istringstream(input) >> mm >> hh >> math[loop];
cout << "hours = " << hh << endl;
times[loop].setTimeHours(hh);
times[loop].setTimeMinutes(mm);
times[loop].show();
cout << "*" << math[loop] << endl;
}
std::cout << "Number of lines in text file: " << number_of_lines << "\n" << endl;
}else{
cout << "Could not open file!!!" << endl;
}
The file I'm reading looks like this:
90 1 3.0
1 1 100.0
2 34 5.1
And the output when I run:
In File: data04.txt
------------------------------------
hours = 0
Operation To Be Done = 0:2336552*1.15384e-317
hours = 0
Operation To Be Done = 0:2336552*1.58101e-322
hours = 0
Operation To Be Done = 0:2336552*1.15397e-317
Number of lines in text file: 3
Anyone know why its not storing the values?
There are several key problems in this code
It doesn't check if inputs are successful. You always need to make sure you verify that the input operations worked before you process the data you read. Failing so will cause random data to be processed.
You first read to the end of the stream and then hope that the stream magically restarted. That won't work. Read the stream just once and keep appending to a std::vector<Time> (or similar container). Aside from only traversing the file once, on UNIXes the file size can change while reading.
C++ doesn't have variable sized arrays although some compiler may offer an extension similar to C's variable sized array. In C++ you'd use a std::vector<Time> instead.
First and foremost, your program is wrong. After the while loop ends, there is nothing more to read in the file (unless you seekg() back to the beginning), so the std::getline() call in the for loop body basically does nothing.
A second problem is that concerns are not properly separated.
Here is how I would have implemented this program:
struct line_data
{
Time t;
double x;
};
// This handles reading a single Time value.
std::istream & operator >> (std::istream & is, Time & t)
{
int hh, mm;
if (is >> hh >> mm)
{
// Not happy with the following two lines, too Java-like. :-(
t.setTimeHours(hh);
t.setTimeMinutes(mm);
}
return is;
}
// This handles reading a single line of data.
std::istream & operator >> (std::istream & is, line_data & ld)
{
std::string s;
if (std::getline(is, s))
{
std::istringstream iss(s);
// Ensure errors are propagated from iss to is.
if (!(iss >> ld.t >> ld.x))
is.setstate(std::ios::failbit);
}
return is;
};
// This handles processing a single line of data.
struct line_manip // satisfies concept OutputIterator<line_data>
{
std::back_insert_iterator<std::vector<Time>> ti;
std::back_insert_iterator<std::vector<double>> xi;
line_manip(std::vector<Time> & ts, std::vector<double> & xs)
: ti(std::back_inserter(ts))
, xi(std::back_inserter(xs))
{
}
line_manip & operator = (const line_data & ld)
{
ti = ld.t;
xi = ld.x;
return *this;
}
line_manip & operator * () { return *this; }
line_manip & operator ++ () { return *this; }
line_manip & operator ++ (int) { return *this; }
};
int main()
{
std::ifstream ifs("input.txt");
std::vector<Time> ts;
std::vector<double> xs;
std::copy(std::istream_iterator<line_data>(ifs),
std::istream_iterator<line_data>(),
line_manip(ts, xs));
// ...
}
as ever I'm fairly new to C++ and I'm not exactly up with the lingo yet either so I apologize for sounding vague in advance!
My problem is I'm struggling to see why my while loop seems to stop the rest of my methods in my overloaded operator function;
#include "sample.h"
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
sample::sample(vector<double> doubles){}
sample::sample() {}
ostream& operator<< (ostream &out, sample &sample)
{
out << "<" << sample.n << ":";
return out;
}
istream& operator>> (istream &in, sample &sample)
{
char firstChar;
in >> firstChar;
if(firstChar != '<'){
cout << "You've not entered the data in a valid format,please try again!1 \n";
exit(1);
}
int n;
in >> n;
sample.n = n;
char nextChar;
in >> nextChar;
if(nextChar != ':'){
cout << "You've not entered the data in a valid format,please try again!2 \n";
exit(1);
}
vector<double> doubles;
double number;
while (in >> number){
doubles.push_back(number);
cout << in << " " << number;
}
in >> lastChar;
return in;
}
int main(void)
{
sample s;
while (cin >> s){
cout << s << "\n";
}
if (cin.bad())
cerr << "\nBad input\n\n";
return 0;
}
My input would be something like;
<6: 10.3 50 69.9 >
I'm trying to get all the doubles after the ':' into a vector, which I can do if they're ints but once a '.' is entered it seems to stop.
If I only put integers in, it also seems to stop after the while(in >> number) has finished finding all the numbers, which is fine but the cout<< command in my main function doesn't seem to work!
Where have I gone wrong?
You have to obey the standard stream idioms: every stream is implicitly convertible to a bool (or void pointer) to allow a check like if (in >> n) to see if the operation succeeded. So first of all you have to make sure that your operator conforms to this (by ensuring that the stream is "good" if the extraction succeeded).
Second, when you write a loop like while (in >> x) { /*...*/ }, then after the loop terminates, you already know that your stream is no longer good. So you'll have to call clear() on it before returning it.
Maybe something like this:
std::istream& operator>> (std::istream &in, sample &sample)
{
char c;
int n;
double d;
std::vector<double> vd;
if (!(in >> c)) { return in; } // input error
if (c != '>') { in.setstate(std::ios::bad); return in; } // format error
if (!(in >> n)) { return in; } // input error
if (!(in >> c)) { return in; } // input error
if (c != ':') { in.setstate(std::ios::bad); return in; } // format error
while (in >> d)
{
vd.push_back(d);
}
in.clear();
if (!(in >> c)) { return in; } // input error
if (c != '>') { in.setstate(std::ios::bad); return in; } // format error
state.n = n;
state.data.swap(vd);
return in;
}
Note that we only modify the sample object if the entire input operation succeeded.
cout << in << " " << number;
you probably meant
cout << " " << number;
or something