C++ how to validate input for use with overloaded >> operator - c++

I am writing a program that can do operations on complex numbers. I have a class called ComplexNumber that has the overloaded operators in it. My program takes input from a file in the form of complex *operator* complex. So, for example an input would look like 3+4i + 2+3i. I have written my >> operator so this works fine.
The issue arises when the input looks like 3i + 1+2i. We have to validate the input so it works when the complex number is missing parts. It can be just a real number, or just an imaginary number.
The functions in the ComplexNumber class that relate to this issue are as follows:
ComplexNumber::ComplexNumber(double r,double i)
{
realNum = r;
imaginaryNum = i;
}
istream& operator>>(istream &input , ComplexNumber& other) //Overloaded >> operator
{
char filter = 0;
double r =0;
double i = 0;
input >> r >> i >> filter;
other.setR(r);
other.setI(i);
return input;
}
And the way I am reading in the input in my main class is as follows:
void input(ifstream &in)
{
ComplexNumber a,b;
in >> a;
in.get();
string op;
getline(in,op,' ');
in >> b;
cout << a << " " << op << " " << b << endl;
}
int main()
{
ifstream in("complex.txt");
if(!in) cout << "failed to open file." << endl;
while(!in.eof()){
input(in);
}
return 0;
}
For my operators to work, I need to set the missing part of the input as 0 in the object. So if the input was 3i the variables in the object would be realNum = 0, imaginaryNum = 3 How can I achieve this?
How can I check the input on the line to decide how it should be read in? At the moment, it is expecting the complex number to have both a real and imaginary part to it.
I also wrote an overloaded constructor for cases where the complex number only has one of the parts to it, but I am unsure how to use it. The function is as follows:
ComplexNumber::ComplexNumber(double in, string r_i) //Overloaded constructor
{
if(r_i == "r"){realNum = in; imaginaryNum = 0;}
else{imaginaryNum = in; realNum = 0;}
}
Beyond this issue, we also have to check to make sure that the input has no invalid characters eg. j or ! but i feel that if I get help with this first problem, I can use the information given to solve this second problem.
I realize that this may not be worded in the best way, I just hope you understand what I am trying to achieve. I really appreciate any help with this. Thanks.

Normally I'd do this with a state machine. Never done it with C++ streams before. Bit sneakier than it looked, but basically the same. Commentary on the whats and whys embedded as comments in the code.
#include <string>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cctype>
// Made this really dumb for ease of writing example
struct ComplexNumber
{
double realNum;
double imaginaryNum;
};
// splitting the guts of the parsing off into its own function made writing
// operator>> dead easy
bool parsecomplex(std::istream &input,
double & real,
double & imag)
{
char filter;
double temp;
char next;
if (input >> temp)// read a double. No clue if it's the real or imaginary part yet.
{
next = input.peek(); // check the next character, but do not extract
if (next != 'i') // not imaginary
{
real = temp; // store as real
if (next == '+' || next == '-') // do we stop here or is there an imaginary?
{
if (input >> imag >> filter // read imaginary
&& filter == 'i') // and ensure trailing i
{
return true;
}
}
else
{
return true;
}
}
else
{ // just an imaginary
imag = temp;
input >> filter; // remove the i. we already know it's an i
return true;
}
}
return false;
}
std::istream& operator>>(std::istream &input,
ComplexNumber& other)
{
double real = 0.0;
double imag = 0.0;
if (parsecomplex(input, real, imag))
{ // OK so we got a good complex number.
other.realNum = real;
other.imaginaryNum = imag;
input.clear(); // may have read eof
return input;
/* This next bit is a deviation from normal stream parsing. Typically 3j
would be read and store of 3 as real and j stays in the stream for the
next read. OP sounds like they might need to be a bit more anal. If so,
replace the above with
char next = input.peek();
if (std::isspace(next) || next == std::char_traits<char>::eof())
{
other.realNum = real;
other.imaginaryNum = imag;
input.clear(); // may have read eof
return input;
}
The Law of Least Surprise says you should go with the expected parsing
behaviour so as to not leave a trail of confused and angry programmers
in your wake. */
}
input.setstate(std::ios::failbit);
return input;
}
// quick test harness
void test(const char * str)
{
ComplexNumber cnum;
std::stringstream input(str);
if (input >> cnum)
{
std::string remaining;
std::getline(input, remaining);
std::cout << str << " is " << cnum.realNum <<","<< cnum.imaginaryNum
<< " still in stream: " << remaining << std::endl;
}
else
{
std::cout << "Invalid: " << str << std::endl;
}
}
int main()
{
test("3-3i");
test("3");
test("-3i");
test(" 3-3i");
test("3-3i ");
test("3 ");
test("-3i ");
test("3-3i 3-3i");
test("3 -3i");
test("j3+3i");
test("3j3i");
test("3+3j");
test("3+3ij");
test("3j");
test("-3j");
test("-3ij");
test("");
test("DETHTONGUE!");
}
Output:
3-3i is 3,-3 still in stream:
3 is 3,0 still in stream:
-3i is 0,-3 still in stream:
3-3i is 3,-3 still in stream:
3-3i is 3,-3 still in stream:
3 is 3,0 still in stream:
-3i is 0,-3 still in stream:
3-3i 3-3i is 3,-3 still in stream: 3-3i
3 -3i is 3,0 still in stream: -3i
Invalid: j3+3i
3j3i is 3,0 still in stream: j3i
Invalid: 3+3j
3+3ij is 3,3 still in stream: j
3j is 3,0 still in stream: j
-3j is -3,0 still in stream: j
-3ij is 0,-3 still in stream: j
Invalid:
Invalid: DETHTONGUE!

Related

How to read data in text file and assign arrays each column c++

I want to learn how to read a text file (contain 3 columns 300000+ rows) and assign each column of the text file to different arrays in c++. I searched this on the internet but i could not find any suitable solution for this. I found a code to do this but the code read only 547 rows. The code is below.
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
int main() {
vector<double> vecX, vecY, vecZ;
double x, y, z;
ifstream inputFile("filetext.txt");
while (inputFile >> x >> y >> z)
{
vecX.push_back(x);
vecY.push_back(y);
vecZ.push_back(z);
}
for(int i = 0; i < vecX.size(); i++) {
cout << vecX[i] << ", " << vecY[i] << ", " << vecZ[i] << endl;
cout << i << endl;
}
}
A sample input text file data is below:
3.862015625000000e+03 5.611499505259664e-01 1.183793839633211e-02
3.862031250000000e+03 5.587474540972663e-01 1.186382272148924e-02
3.862046875000000e+03 7.376678568236076e-01 1.032568525995413e-02
3.862062500000000e+03 8.921759412061890e-01 9.389467084403112e-03
3.862078125000000e+03 8.003829513850249e-01 9.913663338280957e-03
. . .
. . .
. . .
I have one more question. The above code give an output such this: 3862.02, 0.56115, 0.0118379. But i want full digit as in the text file. How can i get.
Thanks in advance.
As mentioned in the comments, you probably have some garbage in the file and you are probably better off storing what you read in one vector.
To be able to find garbage more easily, you could throw an exception when reading fails.
Example:
#include <stdexcept>
// a class to store the values on one line in the file
struct cols_t {
double cols[3];
};
// a function to read one cols_t from a stream
std::istream& operator>>(std::istream& is, cols_t& c) {
return is >> c.cols[0] >> c.cols[1] >> c.cols[2];
}
// a function to read a stream until it's depleated, throwing an exception if it fails:
auto read_stream(std::istream& is) {
std::vector<cols_t> res;
size_t no = 0;
cols_t tmp;
while(is >> tmp) {
res.push_back(tmp);
++no;
}
if(not is.eof()) { // stream not depleated, something is wrong
is.clear(); // clear error state
std::string word;
is >> word; // and read whatever is in the stream
// throw an exception showing where the problem is
throw
std::runtime_error(
"could not read double on line " + std::to_string(no) +
", got \"" + word + "\"."
);
}
return res;
}
int main() {
std::ifstream inputFile("filetext.txt");
try {
auto vec = read_stream(inputFile);
}
catch(const std::exception& ex) {
std::cerr << ex.what() << '\n';
}
}
Setting the precision can be done using the ostreams precision() member function or using std::setprecision().
You also have a few other formatting options like std::fixed and std::scientific
If you want to be able to handle input containing -nan and nan (not a number) you can instead read the file word by word using a std::string and use std::strtod to convert the string to a double. strtod handles nans while formatted input (using >>) does not.
Example:
// a function to read one cols_t from a stream
std::istream& operator>>(std::istream& is, cols_t& c) {
std::string word;
char* end;
for(auto& v : c.cols) {
is >> word;
v = std::strtod(word.c_str(), &end); // convert word to double
if(end == word.c_str()) { // conversion to double failed
is.setstate(std::istream::failbit); // set the failbit on the stream
break; // and break out of the loop
}
}
return is;
}
// a function to read a stream until it's depleated, throwing an exception if it fails:
auto read_stream(std::istream& is) {
std::vector<cols_t> res;
size_t no = 0;
cols_t tmp;
while(is >> tmp) {
res.push_back(tmp);
++no;
}
if(not is.eof()) { // stream not depleated, something went wrong
// throw an exception showing the line with the problem
throw
std::runtime_error(
"could not read double on line " + std::to_string(no)
);
}
return res;
}
Demo

C++ Specify input from istream and test it

I have a class basically representing a tuple (double x, doubly y) and I have overloaded the << operator so I can print the class. Now I want to do the same for >>, so that it only supports following formats:
x, (x) and (x,y).
I jave following code:
std::ostream & operator<< (std::ostream &output, tuple &c){
output << "(" << c.x << "," << c.y << ")" << endl;
return output;
}
std::istream & operator>> (std::istream & input, tuple &c){
// Check for following patterns: x, (x) or (x,y)
}
Can I loop through input and regex match? In that case how?
Also how could I test that it's actually working, something like this
std::cin >> "(10.2,5.5)"
or do I need to read from a file to test?
Edit:
The answer given did solve this problem, but I wanted to add a way to test it as it might be to use of someone other than me:
tuple x(6,2);
stringstream ss;
ss << x;
ASSERT_EQUALS(ss.str(), "(6,2)\n");
Regex would just be unnecessary for a simple input task such as this. Here is how I would do it, without any checking for valid input or not, just parsing:
std::istream & operator>> (std::istream & input, tuple &c){
// Check for following patterns: x, (x) or (x,y)
char firstCharacter;
input >> firstCharacter; // See what the first character is, since it varies
if (firstCharacter == '(')
{ // The simplest case, since the next few inputs are of the form n,n)
char delimiters;
input >> c.x >> delimiters >> c.y >> delimiters;
// N , N )
// You also here have to check whether the above inputs are valid,
// such as if the user enters a string instead of a number
// or if the delimeters are not commas or parentheses
}
else if (isdigit(firstCharacter) || firstCharacter == '-')
{ // For negative numbers
char delimiters;
input.unget(); // Put the number back in the stream and read a number
input >> c.x >> delimiters >> delimiters >> c.y >> delimiters;
// N , ( N )
// You also here have to check whether the above inputs are valid,
// such as if the user enters a string instead of a number
// or if the delimeters are not commas or parentheses
}
else
{
// Handle some sort of a parsing error where the first character
// is neither a parenthesis or a number
}
return input;
}
A little late to the party, but here is regex solution. While it is not pretty, it allows for negative numbers input as well as scientific notation. It also will tolerate spaces between numbers:
#include <iostream>
#include <regex>
#include <tuple>
std::istream &operator>> (std::istream &input, std::tuple<double, double> &t)
{
std::smatch m;
std::string s;
if (std::getline(input, s))
{
if (std::regex_match(s, m, std::regex(R"(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*)"))) //x
t = std::move(std::make_tuple(std::stod(m[1]), 0.0));
else if (std::regex_match(s, m, std::regex(R"(\s*\(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*\)\s*)"))) //(x)
t = std::move(std::make_tuple(std::stod(m[1]), 0.0));
else if (std::regex_match(s, m, std::regex(R"(\s*\(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*,\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*\)\s*)"))) //(x,y)
t = std::move(std::make_tuple(std::stod(m[1]), std::stod(m[2])));
}
return input;
}
int main()
{
std::tuple <double, double> t;
std::cout << "Enter data in format num, (num) or (num1,num2): ";
std::cin >> t;
std::cout << "Tuple 0: " << std::get<0>(t) << std::endl;
std::cout << "Tuple 1: " << std::get<1>(t) << std::endl;
return 0;
}

cin loses first few characters of each line

I've seen other people reporting this problem with cin.ignore() and getline(). I understand it's some problem involving newlines, but I'm not entirely sure how to debug this with >>. I'm trying to implement a gradebook that takes in a student name and test grades and outputs their name (and eventually, course grade) [from Chapter 4 of Accelerated C++]. I'm having trouble even outputting the names properly, though.
// Student.cpp
#include "Student.h"
#include <iostream>
#include <vector>
istream& read(istream& in, Student& s) {
in >> s.name >> s.midterm;
read_hw(in, s.homework);
return in;
}
istream& read_hw(istream& in, vector<double>& hw) {
if (in) {
hw.clear();
double x;
while (in >> x)
hw.push_back(x);
in.clear();
}
return in;
}
And here I try to test it with my main function:
int main() {
vector<Student> students;
Student curr_student;
while (read(cin, curr_student)) {
cout << curr_student.name;
students.push_back(curr_student);
cout << students.size() << endl;
}
cout << students.size() << endl;
for (int i = 0; i < students.size(); i++) {
cout << students[i].name << endl;
}
return 0;
}
When I input something into the command line, though, the output of the student names after the first one are cut off:
Input in terminal:
Alice 50 50 50 50 (<enter>)
Bob 100 100 100 100 (<enter>)
Carl 50 50 50 50 (<enter>)
(<Ctrl-D>)
And then it outputs:
Alice
ob
rl
From the look of the result the grades are read using hex format: B, C, and a are vaild hex digits. That shouldn't be happen according to your cide, though.
In any case note that formatted reading will normally skip all leading white space, including newlines. There are a few approaches to deal with line ends. The usual one is to resd lines into a std::string and use the string to initialize an std::istringstream.
An alternative approach would be the use of a custom definition of line ends by specializing the std::ctype<char> facet to not consider newline a space character. Another approach is using a manipulator which consumes whitespace but sets std::ios_base::failbit upon encountering a newline. For example:
std::istream& skip(std::istream& in) {
if (std::istream::sentry kerberos{in, true}) {
std::istreambuf_iterator<char> it(in), end;
if (end != (it = std::find_if(it, end, [](unsigned char c){
return !std::isspace(c) || char(c) == '\n'; }))
&& *it == '\n') {
++it;
in.setstate(std::ios_base::failbit);
}
}
return in;
}
// ...
if (in) {
while (in >> skip >> x) {
hw.push_back(x);
}
if (!hw.empty()) {
in.clear();
}
}
While I don't think I'd be able that the original code actually reproduces the problem I'm fairly sure that the above approach does fix it!

istringstream not storing anything in variables

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));
// ...
}

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