Overload ifstream in c++ - c++

I am trying to read complex numbers of the form x + y*i from a file which looks like this:
2 + 3i
4 + 5i
If I implement it like this it only works for the first line and I would like to be able to read again in the same manner the second line. Any ideas?
istream& operator>>(istream& in, Complex& c) {
in >> c.r >> c.i;
return in;
}

EDIT: Do not throw exceptions directly, as this is not the usual way of doing thing with iostreams.
EDIT: Process the sign character separately so that spaces in the input are allowed.
A quick and dirty solution:
istream& operator>>(istream& in, Complex& c)
{
char sign;
char suffix;
in >> c.r >> sign >> c.i >> suffix;
if ((sign != '+' && sign != '-') || suffix != 'i') {
in.setstate(ios::failbit);
}
if (sign == '-') {
c.i = -c.i;
}
return in;
}

You need to make sure that you read both the "+" and the "i" when processing the input.
The following implementation works:
struct complex
{
double r;
double i;
};
std::istream& operator>>(std::istream& in, complex& c)
{
char plus, i;
in >> c.r >> plus >> c.i >> i;
return in;
}
std::string initdata = "2 + 3i\n4 + 5i\n";
int main()
{
std::stringstream ss(initdata);
std::vector<complex> values;
std::istream_iterator<complex> begin(ss), end;
std::copy(begin, end, std::back_inserter<std::vector<complex>>(values));
}

I would do it like this :
#include <iostream>
struct complex
{
int r, i;
};
int main ()
{
complex co;
char c;
while ((std::cin >> co.r >> c /* '+' */ >> co.i >> c /* 'i' */))
{
std::cout << co.r << ' ' << co.i << "i\n";
}
}

This should work for you:
#include <iostream>
#include <sstream>
int main(int argc, char *argv[])
{
std::istringstream s(
"2 + 3i\n"
"4 + 5i\n"
);
char sgn;
double r;
double i;
while(s >> r >> sgn >> i) {
if(s.get() != 'i') {
std::cerr << "Missing i\n";
return -1;
}
std::cout << r << sgn << i << "i\n";
}
return 0;
}
Note: The space before the imaginary part and the trailing i are breaking the input.

Related

How to parse in this file in c++ dealing with spaces and new lines?

Credit 1 2 150 12345678 10-10-2020 123
Cash 2 3 199 200 1
Check 1 3 100 111000614 124356499
Credit 2 1 50 987654321 10-10-2021 321
I am trying to read in this file in c++ but I can't figure out a proper way to do it. I need to read in each individual data point into different vectors. For example in a vector called transaction type would be Credit cash check Credit.
This is the code I have now and it gives me really weird results
file.open(fileName);
string line;
string space;
while(getline(file,line,'\n')){
cout<<"Row: ";
while(getline(file,space,' ')){
cout<<space<<" ";
}
}
cout<<space<<" ";
return 0;```
Spaces and newlines are classified as (white)space in C++, see std::isspace. std::istream& operator>>(std::istream&, T&) skips all whitespace, so that no special handling for spaces or newlines is required.
One example:
#include <iostream>
#include <string>
#include <vector>
struct Date {
unsigned yyyymmdd;
};
std::istream& operator>>(std::istream& s, Date& date) {
std::string t;
s >> t;
int dd = std::stoi(t.substr(0, 2));
int mm = std::stoi(t.substr(3, 2));
int yyyy = std::stoi(t.substr(6, 4));
date.yyyymmdd = yyyy * 10000 + mm * 100 + dd;
return s;
}
struct Credit {
int a, b, c, d, f;
Date e;
};
std::istream& operator>>(std::istream& s, Credit& c) {
return s >> c.a >> c.b >> c.c >> c.d >> c.e >> c.f;
}
struct Cash {
int a, b, c, d, e;
};
std::istream& operator>>(std::istream& s, Cash& c) {
return s >> c.a >> c.b >> c.c >> c.d >> c.e;
}
struct Check {
int a, b, c, d, e;
};
std::istream& operator>>(std::istream& s, Check& c) {
return s >> c.a >> c.b >> c.c >> c.d >> c.e;
}
template<class V>
void load_element(V& v, std::istream& s) {
v.emplace_back();
s >> v.back();
}
struct Data {
std::vector<Credit> credits;
std::vector<Cash> cashs;
std::vector<Check> checks;
Data(std::istream& s) {
for(std::string type; s >> type;) {
if(type == "Credit")
load_element(credits, s);
else if(type == "Cash")
load_element(cashs, s);
else if(type == "Check")
load_element(checks, s);
else
throw;
}
}
};
int main() {
std::istream& file = std::cin;
Data d(file);
std::cout << "credits: " << d.credits.size() << '\n';
std::cout << "cashs: " << d.cashs.size() << '\n';
std::cout << "checks: " << d.checks.size() << '\n';
}
Run it as:
./test < input.txt
input.txt contains the 4 input lines from your post.
Output:
credits: 2
cashs: 1
checks: 1
Cash in uncountable in English, however, in programming one likes to distinguish scalars and vectors/containers, hence s suffix for a vector of cash. Some of my colleagues go as far as just using suffix s for all containers regardless of arbitrary irregular rules of English, e.g. currency for scalars and currencys for vectors.

how can I input a literal string into istream? [duplicate]

How to achieve scanf("%d # %d",&a,&b);sort of effect with cin in C++ ?
You can skip the # by extracting it into a character:
std::istringstream iss("10 # 20");
int main()
{
int a, b; char hash;
iss >> a >> hash >> b;
assert(a == 10 && b == 20);
}
You could create your own stream manipulator. It is fairly easy.
#include <ios>
#include <iostream>
using namespace std;
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};
std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;
char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}
There isn't a direct function inside the istream class that mimics it, unfortunately. There are functions that you might be able to use to manipulate the stream and get the correct input, but I'm not familiar with how they work so I couldn't tell you how.
My best suggestion on how I would personally do it is to use getline() to put the input into a string and then from there I would do a few checks to see if it matches the format. So in your case I would grab the first substring up until the first space, make sure it's a valid decimal, check to make sure the pound sign ('#') is in the correct spot, then grab the ending number to make sure it's valid. If any one of those three objects are incorrect I would set some boolean variable to false to kick out or return or something to indicate that the input was invalid and not the correct format.
Pseudo Code:
...
getline(cin,myStr);
while(!formatCheck(myStr))
{
cout<<"Not valid format for input";
getline(cin,myStr);
}
...
bool formatCheck(string str)
{
string firstPart=str.subString(0,firstSpaceLocation);
string middle=str[firstSpaceLocation+1];
string lastPart=str.subString(firstSpaceLocation+3,end);
if(first part not a valid number || middle!="#" || last part not a valid number)
{
return false;
}
return true;
}
Here's another way. You can classify # as a whitespace character through the std::ctype<char> facet imbued in the locale:
#include <iostream>
#include <sstream>
#include <vector>
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
is.imbue(std::locale(is.getloc(),
new detail::ctype(manip.m_ws, manip.m_opt)));
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10 # 20");
int a, b;
iss >> setws("#");
iss >> a >> b;
iss >> unsetws("#");
std::cout << a << ' ' << b; // 10 20
}
You can skip the #, or any single character, by using std::istream::ignore
std::istringstream sstr("1024 # 768");
int main()
{
int a, b;
sstr >> a;
sstr.ignore(256,'#'); // ignore until hash character
sstr >> b;
std::cout << "a: " << a << " b: " << b << std::endl;
}

Making a custom istream manipulator

I want to make a custom istream manipulator that reads 2 characters from the input, then skips 2 characters from the input, and does that until it runs out of any input.
For example, if I have code like this:
std::string str;
std::cin >> skipchar >> str;
Where skipchar is my manipulator, if the user enters 1122334455, str should contain 113355.
This is what I've got so far, I don't know what I should put in the while loop condition to make this code work properly:
istream& skipchar(istream& stream)
{
char c;
while(1)
{
for (int i = 0; i < 2; ++i)
stream >> c;
for (int i = 0; i < 2; ++i)
stream.ignore(1, '\0');
}
return stream;
}
Any help would be appreciated.
That's a very nice question. I don't know if it's possible. But I implemented something different which gives you the same short syntax you wanted, by overloading the >> operator with a new class called Skip2. Here is the code (which I really enjoyed writing! :-) )
#include <iostream>
#include <string>
#include <istream>
#include <sstream>
using namespace std;
class Skip2 {
public:
string s;
};
istream &operator>>(istream &s, Skip2 &sk)
{
string str;
s >> str;
// build new string
ostringstream build;
int count = 0;
for (char ch : str) {
// a count "trick" to make skip every other 2 chars concise
if (count < 2) build << ch;
count = (count + 1) % 4;
}
// assign the built string to the var of the >> operator
sk.s = build.str();
// and of course, return this istream
return s;
}
int main()
{
istringstream s("1122334455");
Skip2 skip;
s >> skip;
cout << skip.s << endl;
return 0;
}
It's tricky; istream manipulators don't operate as "filters" on the stream but rather as single-shot operations. The istream manipulators provided by the Standard (noskipws, hex etc.) do their work by setting and clearing flags on the stream, so they only expose functionality that is already available.
However, it is possible to create a filtering streambuf wrapping the streambuf of cin (or any input stream) and use a manipulator to install or remove it:
struct skipbuf : std::streambuf {
std::unique_ptr<std::streambuf> src;
int i;
char buf[4];
skipbuf(std::streambuf* src) : std::streambuf{*src}, src{src} {
setg(buf, buf + 2, buf + 2);
}
std::streambuf* unwrap() {
while (buf + i != gptr())
src->sputbackc(buf[--i]);
return src.release();
}
std::streambuf::int_type underflow() override {
setg(buf, buf, buf + std::min(i = src->sgetn(buf, 4), 2));
return i ? buf[0] : traits_type::eof();
}
};
std::istream& skipchar(std::istream& is) {
is.rdbuf(new skipbuf{is.rdbuf()});
return is;
}
std::istream& noskipchar(std::istream& is) {
if (auto* buf = dynamic_cast<skipbuf*>(is.rdbuf()))
delete (is.rdbuf(buf->unwrap()), buf);
return is;
}
Example of usage:
int main() {
std::istringstream iss{"1122334455 hello"};
std::string s1, s2;
iss >> skipchar >> s1 >> noskipchar >> s2;
std::cout << s1 << ' ' << s2 << std::endl;
}
Expected output (run it online):
113355 hello

Skipping expected characters like scanf() with cin

How to achieve scanf("%d # %d",&a,&b);sort of effect with cin in C++ ?
You can skip the # by extracting it into a character:
std::istringstream iss("10 # 20");
int main()
{
int a, b; char hash;
iss >> a >> hash >> b;
assert(a == 10 && b == 20);
}
You could create your own stream manipulator. It is fairly easy.
#include <ios>
#include <iostream>
using namespace std;
// skips the number of characters equal to the length of given text
// does not check whether the skipped characters are the same as it
struct skip
{
const char * text;
skip(const char * text) : text(text) {}
};
std::istream & operator >> (std::istream & stream, const skip & x)
{
ios_base::fmtflags f = stream.flags();
stream >> noskipws;
char c;
const char * text = x.text;
while (stream && *text++)
stream >> c;
stream.flags(f);
return stream;
}
int main()
{
int a, b;
cin >> a >> skip(" # ") >> b;
cout << a << ", " << b << endl;
return 0;
}
There isn't a direct function inside the istream class that mimics it, unfortunately. There are functions that you might be able to use to manipulate the stream and get the correct input, but I'm not familiar with how they work so I couldn't tell you how.
My best suggestion on how I would personally do it is to use getline() to put the input into a string and then from there I would do a few checks to see if it matches the format. So in your case I would grab the first substring up until the first space, make sure it's a valid decimal, check to make sure the pound sign ('#') is in the correct spot, then grab the ending number to make sure it's valid. If any one of those three objects are incorrect I would set some boolean variable to false to kick out or return or something to indicate that the input was invalid and not the correct format.
Pseudo Code:
...
getline(cin,myStr);
while(!formatCheck(myStr))
{
cout<<"Not valid format for input";
getline(cin,myStr);
}
...
bool formatCheck(string str)
{
string firstPart=str.subString(0,firstSpaceLocation);
string middle=str[firstSpaceLocation+1];
string lastPart=str.subString(firstSpaceLocation+3,end);
if(first part not a valid number || middle!="#" || last part not a valid number)
{
return false;
}
return true;
}
Here's another way. You can classify # as a whitespace character through the std::ctype<char> facet imbued in the locale:
#include <iostream>
#include <sstream>
#include <vector>
namespace detail
{
enum options { add, remove };
class ctype : public std::ctype<char>
{
private:
static mask* get_table(const std::string& ws, options opt)
{
static std::vector<mask> table(classic_table(),
classic_table() + table_size);
for (char c : ws)
{
if (opt == add)
table[c] |= space;
else if (opt == remove)
table[c] &= ~space;
}
return &table[0];
}
public:
ctype(const std::string& ws, options opt)
: std::ctype<char>(get_table(ws, opt)) { }
};
}
class adjustws_impl
{
public:
adjustws_impl(const std::string& ws, detail::options opt) :
m_ws(ws),
m_opt(opt)
{ }
friend std::istream& operator>>(std::istream& is,
const adjustws_impl& manip)
{
is.imbue(std::locale(is.getloc(),
new detail::ctype(manip.m_ws, manip.m_opt)));
return is;
}
private:
std::string m_ws;
detail::options m_opt;
};
adjustws_impl setws(const std::string& ws)
{
return adjustws_impl(ws, detail::add);
}
adjustws_impl unsetws(const std::string& ws)
{
return adjustws_impl(ws, detail::remove);
}
int main()
{
std::istringstream iss("10 # 20");
int a, b;
iss >> setws("#");
iss >> a >> b;
iss >> unsetws("#");
std::cout << a << ' ' << b; // 10 20
}
You can skip the #, or any single character, by using std::istream::ignore
std::istringstream sstr("1024 # 768");
int main()
{
int a, b;
sstr >> a;
sstr.ignore(256,'#'); // ignore until hash character
sstr >> b;
std::cout << "a: " << a << " b: " << b << std::endl;
}

Overloading operator help?

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