C++: Splicing a String within cin Overload - c++

I am trying to splice a cin>> string; which should be a complex number.
I am working within the class Complex, which deals with complex numbers.
I'm having a hard time splicing the string into separate components so I can arrange each substring and convert them into relevant doubles that correspond to imaginary or realpart.
When I run the code there is an error with how I am finding the string I think, it appears to be out of bounds.
This is the header and the definition of my function, overloading >>.
Header:
friend istream& operator >>(istream& inputStream, Complex& amt);
Definition:
istream& operator >>(istream& inputStream, Complex& amt){
cout<<"Please enter your complex number: ";
std::string str;
inputStream >> str;
//need different parts of string. imaginary, real, operand, 'i', and beginning sign.
std::size_t pos = str.rfind('-'|'+');
std::string str2 = str.substr(pos); // str2 = imaginarypart
if(str2.back()=='i'){
str2.erase( str2.end()-1 );
}
if(str2[0]=='+'){
str2.erase( str2.begin()-1 );
}
//str remains with realpart
return inputStream;
}

If you're merely looking for a way to input complex numbers, in any suitable format, I'm proposing a way simpler solution, using std::complex from the standard library: http://en.cppreference.com/w/cpp/numeric/complex/operator_ltltgtgt
Something like:
#include <complex>
#include <string>
#include <iostream>
bool
inputComplexNumber (std::istream& is, std::complex<double>& c)
{
std::cout << "Please enter your complex number: ";
is >> c;
return is.good();
}
int
main ()
{
std::complex<double> c;
if (inputComplexNumber(std::cin, c))
std::cout << c.real() << (c.imag() > 0 ? "+" : "") << c.imag() << "i" << std::endl;
else
std::cout << "Bad complex number." << std::endl;
}
Sample runs:
Please enter your complex number: (-69.1, 42.2)
-69.1+42.2i
Please enter your complex number: blah
Bad complex number.
If you really do want to stick to the pretty format on input, then I would recommend that you write a little grammar for this mini-language, and derive proper scanning and parsing functions from that. That's a bit of work, but it's well worth it in the end.

Related

Reading specific words from a file and storing them in an object

I'm having trouble coding and conceptualizing this project I was assigned. I've looked around for answers to this issue but had little to no luck, maybe it's really obvious. I'm supposed to prompt the user to a filename, the file is assume to have the following format:
Animal:
Name: [value]
Noise: [value]
Legs: [value]
(with no spaces in between)
It should be able to read as many "animal objects" as there are in the file and store them in an animal object class that has 3 parameters (name, noise, legs).
My issue is mostly during the reading in of the file, I can't figure out a good method for reading the file AND storing the information. Here is the code I currently have. Any help with the code I currently have and ideas for storing the values. Sorry if I explained anything poorly, please ask to clarify if I did, thank you in advance.
cout << "Enter the file name: ";
string fileName;
getline(cin, fileName);
cout << endl;
try
{
ifstream animalFile(fileName);
if (!animalFile.good()) // if it's no good, let the user know and let the loop continue to try again
{
cout << "There was a problem with the file " << fileName << endl << endl;
continue;
}
string line;
while (animalFile >> line) // To get you all the lines.
{
getline(animalFile, line); // Saves the line in STRING.
cout << line << endl; // Prints our STRING.
}
}
catch (...)
{
cout << "There was a problem with the file " << fileName << endl << endl;
}
If you're really binded with this file format, consider doing the following to read the data and store it:
#1. Define a class Animal to represent an animal:
struct Animal
{
std::string name;
int legs;
int noise;
}
#2. Define an istream& operator >> (istream&, Animal&) to read one object of this type and check for the correctness of the input.
std::istream& operator >> (std::istream& lhs, Animal& rhs)
{
std::string buf;
lhs >> buf >> buf >> rhs.name >> buf >> rhs.noise >> buf >> rhs.legs;
}
#3. Use std::copy and std::istream_iterator to read all the values from the file to std::vector:
std::istream_iterator<Animal> eos;
std::istream_iterator<Animal> bos(animalFile);
std::vector<Animal> zoo;
std::copy(bos, eos, std::back_inserter(zoo));
This code has no checking for input errors, which may easily be added into istream& operator >> (istream&, Animal&).

C++ structures/arrays [duplicate]

This question already has answers here:
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Closed 9 years ago.
I'm creating a program that stores basic data. After inputing data for the first 'person', the program simultaneously loops to the second instance of i without accepting any input. This is hard to explain but I hope someone can help me out. I have a feeling that getline and cin.ignore are causing these problems. I only came across them on this website.
struct info {
string name;
string address;
int phone;
};
int main(){
int input;
cout<<"How many people do you want on the list" <<endl;
cin>>input;
info arr[input];
for(int i=0;i<input;i++){
cout<<"Enter name for person " <<i+1 <<": ";
getline(cin,arr[i].name);
cin.ignore(1000,'\n');
cout<<"Enter the address of " <<arr[i].name <<endl;
getline(cin,arr[i].address);
cin.ignore(1000, '\n');
cout<<"Enter phone number of " <<arr[i].name <<endl;
cin>>arr[i].phone;
cout<<endl;
}
}
Mixing formatted input with unformatted input is always asking for trouble. When you extract input into the stream, a newline is always appended. The newline character is the delimiter for std::getline(), meaning whilst extraction is being performed, if std::getline() finds a newline, it discards that character and stops input.
This is the problem you're running into. After the last formatted extraction cin >> input, the newline is left in the input stream. std::getline() (unlike formatted input functions) do not discard leading whitespace, so it is prone to the disadvantage I explained above.
To solve this issue, you need to discard leading whitespace manually, so the the non-whitespace input is ready to be extracted by the time std::getline() is invoked:
std::getline(std::cin >> std::ws, arr[i].name);
// ^^^^^^^^^^^^^^^^^^^
std::ws is manipulator which extracts and discards leading whitespace. This should be satisfactory for your purposes.
The following are suggestions that I advise you take. They lead to a substantially cleaner, clearer code structure. And it also reduces a lot of headaches:
1. Your class should have its own inserter/extractor.
You should define your own inserter/extractor to encapsulate I/O semantics for your class. It facilitates a lot of the trouble of manual extraction. Inserters/extractors are typically implemented as friends of the target class so that it may access private data members. Your class in particular has no private data members, but this is still an idiomatic technique nontheless:
struct info
{
string name;
string address;
int phone;
friend std::ostream& operator<<(std::ostream&, const info&);
friend std::istream& operator>>(std::istream&, info&);
};
Inserters/extractors are global functions because they are being defined in terms of (not only) the target class, but also the stream object itself.
2. Use std::vector<T> rather than raw C-style arrays
The following is wrong:
cin >> input;
info arr[input];
This code instantiates an array at compile-time with a size known only at runtime. The possibility of your compiler accepting this code fully depends on how standard-conformant it is. GCC has a non-standard extension which welcomes code like that, but it is non-standard even so and shouldn't be used.
If you need an array of objects whose size is known at runtime, use a vector of those objects. A vector class is already provided for you through the Standard library:
std::cin >> input;
std::vector<info> arr(input);
In fact, the size is superfluous since the vector will grow dynamically as more and more elements are created. You can simply default-construct the vector (unless you have a good reason to specify the size, in which case that many info objects will be default-constructed in the internal buffer of the vector).
std::vector<info> arr;
This is mostly it. Here is an example of your program that applies these two suggestions:
#include <iostream>
#include <vector>
struct info
{
std::string name;
std::string address;
int phone;
friend std::ostream& operator<<(std::ostream&, const info&);
friend std::istream& operator>>(std::istream&, info&);
};
std::ostream& operator<<(std::ostream& os, const info& obj)
{
return os << "Person's name is: " << obj.name << std::endl
<< obj.name << "'s address is: " << obj.address << std::endl
<< obj.name << "'s phone number is: " << obj.phone << std::endl;
}
std::istream& operator>>(std::istream& is, info& obj)
{
std::cout << "Enter name for person " << ": ";
std::getline(is >> std::ws, obj.name);
std::cout << "Enter the address of " << obj.name << std::endl;
std::getline(is >> std::ws, obj.address);
std::cout << "Enter phone number of " << obj.name << std::endl;
std::cin >> obj.phone;
std::cout << endl;
return is;
}
int main()
{
int input;
std::cout << "How many people do you want on the list" << std::endl;
std::cin >> input; // just as a formality, but really the user can enter
// as much as they want.
// The value of input will be ignored.
std::vector<info> arr;
info obj;
while (std::cin >> obj)
{
arr.push_back(obj);
}
for (const auto& item : arr)
std::cout << item << std::endl;
}
If you have any questions, please leave them as a comment and I will try to help you as best as possible.

What exactly does stringstream do?

I am trying to learn C++ since yesterday and I am using this document: http://www.cplusplus.com/files/tutorial.pdf (page 32). I found a code in the document and I ran it. I tried inputting Rs 5.5 for price and an integer for quantity and the output was 0.
I tried inputting 5.5 and 6 and the output was correct.
// stringstreams
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main ()
{
string mystr;
float price = 0;
int quantity = 0;
cout << "Enter price: ";
getline (cin,mystr);
stringstream(mystr) >> price;
cout << "Enter quantity: ";
getline (cin,mystr);
stringstream(mystr) >> quantity;
cout << "Total price: " << price*quantity << endl;
return 0;
}
What exactly does the mystring command do? Quoting from the document:
"In this example, we acquire numeric values from the standard input
indirectly. Instead of extracting numeric values directly from the
standard input, we get lines from the standard input (cin) into a
string object (mystr), and then we extract the integer values from
this string into a variable of type int (quantity)."
My impression was that the function will take an integral part of a string and use that as input.
Sometimes it is very convenient to use stringstream to convert between strings and other numerical types. The usage of stringstream is similar to the usage of iostream, so it is not a burden to learn.
Stringstreams can be used to both read strings and write data into strings. It mainly functions with a string buffer, but without a real I/O channel.
The basic member functions of stringstream class are
str(), which returns the contents of its buffer in string type.
str(string), which set the contents of the buffer to the string argument.
Here is an example of how to use string streams.
ostringstream os;
os << "dec: " << 15 << " hex: " << std::hex << 15 << endl;
cout << os.str() << endl;
The result is dec: 15 hex: f.
istringstream is of more or less the same usage.
To summarize, stringstream is a convenient way to manipulate strings like an independent I/O device.
FYI, the inheritance relationships between the classes are:
From C++ Primer:
The istringstream type reads a string, ostringstream writes a string, and stringstream reads and writes the string.
I come across some cases where it is both convenient and concise to use stringstream.
case 1
It is from one of the solutions for this leetcode problem. It demonstrates a very suitable case where the use of stringstream is efficient and concise.
Suppose a and b are complex numbers expressed in string format, we want to get the result of multiplication of a and b also in string format. The code is as follows:
string a = "1+2i", b = "1+3i";
istringstream sa(a), sb(b);
ostringstream out;
int ra, ia, rb, ib;
char buff;
// only read integer values to get the real and imaginary part of
// of the original complex number
sa >> ra >> buff >> ia >> buff;
sb >> rb >> buff >> ib >> buff;
out << ra*rb-ia*ib << '+' << ra*ib+ia*rb << 'i';
// final result in string format
string result = out.str()
case 2
It is also from a leetcode problem that requires you to simplify the given path string, one of the solutions using stringstream is the most elegant that I have seen:
string simplifyPath(string path) {
string res, tmp;
vector<string> stk;
stringstream ss(path);
while(getline(ss,tmp,'/')) {
if (tmp == "" or tmp == ".") continue;
if (tmp == ".." and !stk.empty()) stk.pop_back();
else if (tmp != "..") stk.push_back(tmp);
}
for(auto str : stk) res += "/"+str;
return res.empty() ? "/" : res;
}
Without the use of stringstream, it would be difficult to write such concise code.
To answer the question. stringstream basically allows you to treat a string object like a stream, and use all stream functions and operators on it.
I saw it used mainly for the formatted output/input goodness.
One good example would be c++ implementation of converting number to stream object.
Possible example:
template <class T>
string num2str(const T& num, unsigned int prec = 12) {
string ret;
stringstream ss;
ios_base::fmtflags ff = ss.flags();
ff |= ios_base::floatfield;
ff |= ios_base::fixed;
ss.flags(ff);
ss.precision(prec);
ss << num;
ret = ss.str();
return ret;
};
Maybe it's a bit complicated but it is quite complex. You create stringstream object ss, modify its flags, put a number into it with operator<<, and extract it via str(). I guess that operator>> could be used.
Also in this example the string buffer is hidden and not used explicitly. But it would be too long of a post to write about every possible aspect and use-case.
Note: I probably stole it from someone on SO and refined, but I don't have original author noted.
You entered an alphanumeric and int, blank delimited in mystr.
You then tried to convert the first token (blank delimited) into an int.
The first token was RS which failed to convert to int, leaving a zero for myprice, and we all know what zero times anything yields.
When you only entered int values the second time, everything worked as you expected.
It was the spurious RS that caused your code to fail.

error: no match for 'operator>>' in 'std::cin >> stopat'

I'm trying to get back into C++, and this is my second program in a long while. Everything compiles just peachy, until it gets to cin >> stopat; where it returns what seems to be a fairly common error: error: no match for 'operator>>' in 'std::cin >> stopat'
I've looked through a few things explaining what causes this, but nothing I actually understand (due to my relative inexperience in programming). What causes this error, and how do I fix it in case I come across it again?
#include <iostream>
#include "BigInteger.hh"
using namespace std;
int main()
{
BigInteger A = 0;
BigInteger B = 1;
BigInteger C = 1;
BigInteger D = 1;
BigInteger stop = 1;
cout << "How Many steps? ";
BigInteger stopat = 0;
while (stop != stopat)
{
if (stopat == 0)
{
cin >> stopat;
cout << endl << "1" << endl;
}
D = C;
C = A + B;
cout << C << endl;
A = C;
B = D;
stop = stop + 1;
}
cin.get();
}
EDIT: Somehow, I didn't think to link the libraries referenced. Here they are: https://mattmccutchen.net/bigint/
You haven't shown us the code for BigInteger, but there would need to be a function defined (either in BigInteger.hh or in your own code) like this:
std::istream& operator >>(std::istream&, BigInteger&);
This function would need to be implemented to actually get a "word" from a stream and try to convert it to a BigInteger. If you're lucky, BigInteger will have a constructor that takes a string, in which case it would be like this:
std::istream& operator >>(std::istream& stream, BigInteger& value)
{
std::string word;
if (stream >> word)
value = BigInteger(word);
}
Edit: Now that you have pointed out the library that's being used, here's what you can do. The library itself should probably do this for you, since it provides the corresponding ostream operator, but if you look into that you will see that general-purpose, library-quality stream operators are more complex than what I'm writing here.
#include <BigIntegerUtils.hh>
std::istream& operator >>(std::istream& stream, BigInteger& value)
{
std::string word;
if (stream >> word)
value = stringToBigInteger(word);
}
What you've left out here is details about your BigInteger class. In order to read one from an input stream with the >> operator, you need to define operator>> (often called a stream extractor) for your class. That's what the compiler error you're getting means.
Essentially, what you need is a function that looks like this:
std::istream &operator>>(std::istream &is, BigInteger &bigint)
{
// parse your bigint representation there
return is;
}

Reading to end of file with istream_iterator and istream overload

I'm having some trouble reading data from a file into a vector of Orders.
Code:
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <iterator>
using namespace std;
class Purchase;
class Order {
public:
string name;
string address;
vector<Purchase> items;
};
class Purchase {
public:
string product_name;
double unit_price;
int count;
Purchase() {}
Purchase(string pn, double up, int c) :product_name(pn), unit_price(up), count(c) {}
};
istream& operator>>(istream& in, Order& o)
{
string p_name;
double u_price;
int p_count;
getline(in, o.name);
getline(in, o.address);
getline(in, p_name);
in >> u_price >> p_count;
o.items.push_back(Purchase(p_name, u_price, p_count));
return in;
}
ostream& operator<<(ostream& out, const Purchase& p)
{
out << p.product_name << '\n'
<< p.unit_price << '\n'
<< p.count << '\n';
return out;
}
ostream& operator<<(ostream& out, const Order& o)
{
out << '\n' << o.name << '\n'
<< o.address << '\n'
<< o.item << '\n';
return out;
}
int main()
{
cout << "Enter file to read orders from: \n";
string file;
cin >> file;
ifstream is(file.c_str());
istream_iterator<Order> ii(is);
istream_iterator<Order> eos;
ostream_iterator<Order> oo(cout);
vector<Order> orders(ii, eos);
copy(orders.begin(), orders.end(), oo);
}
I have 3 main questions.
1) When I take out the o.item bug in the ostream overload to test output, it only outputs the first entry in the file. The txt file is structured in groups of 5 lines of data that are supposed to be read into vector orders.
Right now the txt file has 10 "orders", but it only reads the first one into the orders vector. I probably need to implement some kind of end of file operation, but I'm not sure how to do this with the istream overload and iterator. This is the biggest problem and if I can figure this out I think I'll probably be okay with the next 2 questions.
2) When that problem is fixed. I will need to deal with the output of o.item (the vector of Purchases in orders which currently can't be output because there is no element being specified). Obviously I need to specify the element to output and I've considered just using a static int and incrementing it, but this would need to be reset for every separate Order, which leads to question 3...
3) If the same name/address are read in as a previous read, I need the program to understand that it is the same "order" being read in and to simply add another object to that Order's Purchases vector rather than creating a new order. I'm thinking about using find() to check if that name already exists in order, and in that case doing nothing with the name/address inputs, but if there is a better way I'd like to know.
Sorry if this is kind of long. If more explanation is needed I'd be happy to elaborate. Any help is appreciated. Thanks.
P.S. Here is an example of input output at the moment if I specify the o.item output to be o.item[0].
Text file has:
John Smith
117 One Tree Hill
Trampoline
600.00
1
//... 9 more Orders like this
Output is:
John Smith
117 One Tree Hill
Trampoline
600.00
1
//... Nothing after this....
Regarding question #3, you could use a multimap instead of a vector.
First, assume you split your Order class up as follows:
class Customer{
public:
string name;
string address;
};
class Purchase {
public:
string product_name;
double unit_price;
int count;
Purchase() {}
Purchase(string pn, double up, int c) :product_name(pn), unit_price(up), count(c) {}
};
class Order {
Customer c;
std::vector<Purchase> p;
};
Now you can simply create a std::multimap<Customer, Purchase>. Adding a customer/purchase pair does exactly what you want: If the customer doesn't already exist, he is added, otherwise the purchase is just added to the existing customer.
Of course, for this to work, you need to define a comparer as well. Simplest way might just be to define operator < for the Customer class. Implement it by comparing the name and disregarding the address.
As for your other questions, avoid mixing getline and stream_iterators. It's not wrong per se, but it gets pretty tricky because getline reads a line at a time, and stream iterators just read to the next whitespace.
Honestly, the C++ IOStreams library is pretty awful to use in general. Since your data format is already cleanly line-separated already, I'd probably just ditch the stream iterators and use getline everywhere.
I haven't looked at your code in detail, but I will give one sentence of advice:
"Do not mix formatted and unformatted input. And in fact, do not use formatted input from files or user input at all."
OK, that was two sentences.
The problem you have is very simple. In fact your code is pretty clear :)
All what you have to add are those simple lines:
istream& operator>>(istream& in, Order& o)
{
string p_name;
double u_price;
int p_count;
getline(in, o.name);
getline(in, o.address);
getline(in, p_name);
in >> u_price >> p_count;
//
while(in.peek() == '\n' || in.peek() == ' ')
{
in.ignore();
}
//
o.items.push_back(Purchase(p_name, u_price, p_count));
return in;
}
The reason is that when using >> it leaves the newline character in the stream unlike getline. You can search Stackoverflow about streams in general there are a lot of great explanations about the issue.
Also, you don't have anything called item in Order. What you have is a vector of Purchase:
ostream& operator<<(ostream& out, const Order& o)
{
out << '\n' << o.name << '\n'
<< o.address << '\n';
//
for(vector<Purchase>::const_iterator i = o.items.begin();
i != o.items.end(); i++)
{
out << *i << '\n';
}
//
return out;
}