Question may seem like a duplicate but I have researched and found nothing that would actually answer my question.
So I have a task that let's the user input groceries as a string, the weight and price as doubles. Simple enough, right? Well, the problem is that the user will enter it in the following format:
Enter product 1: Potato; 5 kg; 49 kr;
Enter product 2: Carrot; 0.5 kg; 13 kr;
Enter product 3: Onion; 0.1 kg; 2 kr;
And then the products will be sorted in a specific manner. However that is not what I need help with, I have a problem with reading in only "Potato", "5" and "49" and store them in seperate variables.
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
using namespace std;
struct Product_Type
{
string name;
double weight;
double price;
};
void get (Product_Type & p)
{
cin >> p.name;
cin.ignore(1);
cin >> p.weight;
cin.ignore(2);
cin >> p.price;
cin.ignore(2);
}
If I do it like this, I will store "Potato;" and not "Potato" in my name variable. So my question is how do I manipulate a string to only read to a certain index and store the content in a variable? I don't want to read the actual semi-colons. Is there a simple way to do this?
You need to understand how formatted input and unformatted input works.
Please read here about it.
You do not need ignore at all. You can do all with simple statements.
But the precondition is that the input matches your specification. Any input of wrong data leads to a problem. So, let's assume that the input follows the above pattern. OK.
Regarding your problem, where you read "potato;" instead of "potato". This is because you use a formatted input function >> to read a string. And this function will stop, if it sees a white space and not before. It will also ignore leading white space by default, but that is not important here.
To solve the problem to read a string until a certain delimiter, you can use the unformatted input function std::getline. Please see here.
So, to get the name, you may write std::getline(std::cin, p.name, ';');. This will read the name from the input stream, including the semicolon, but store the pure name, without the semicolon in the string.
Next we are talking about reading 5 kg; 49 kr. We want to have the "weight". We can simply use formatted input function >> to get it. There is a white space after that, so, very simple. You can write std::cin >> p.weight; to get it.
Next we have to read kg; 49 kr. So, we will use a dummy string and with that can read the "kg;" which we do simple not use. So, with a dummy string "s1", we can write std::cin >> s1;. The kg; will be gone from the stream.
Now we see 49 kr;. Reading the price is again simple using >>. We will write std::cin >> p.price; and have it.
Last but not least we need to read the rest of the line, including the enter. For that we use again std::getline. No need to specify any delimiter, because the standard delimiter is already '\n'.
Then the full code looks like the below:
#include <iostream>
#include <string>
struct Product_Type
{
std::string name;
double weight;
double price;
};
void get(Product_Type& p)
{
std::string s1, s2;
std::getline(std::cin, p.name, ';');
std::cin >> p.weight;
std::cin >> s1;
std::cin >> p.price;
std::getline(std::cin, s2);
}
int main() {
Product_Type pt;
get(pt);
std::cout << pt.name << " --> " << pt.weight << " --> " << pt.price << '\n';
return 0;
}
But this is not considered to be good. There may be errors in the input and with that the failbit of std::cin would be set and you cannot continue to read.
Therefore, normally, we would read first the complete line with std::getline which will most likely always work. Then, we "convert" the string to a stream using an std::istringstream to be able to extract the data from there. If the complete line has an error, then only the temporary std::istringstream will be gone. So this approach is a little bit more robust.
This could then look like:
#include <iostream>
#include <string>
#include <sstream>
struct Product_Type
{
std::string name;
double weight;
double price;
};
void get(Product_Type& p)
{
std::string line, s1, s2;
std::getline(std::cin, line);
std::istringstream iss(line);
std::getline(iss, p.name, ';');
iss >> p.weight;
iss >> s1;
iss >> p.price;
std::getline(iss, s2);
if (not iss)
std::cerr << "\nError: Problem with input\n\n";
}
int main() {
Product_Type pt;
get(pt);
std::cout << pt.name << " --> " << pt.weight << " --> " << pt.price << '\n';
return 0;
}
And a little bit more advanced with chaining the io-functions, we can write
#include <iostream>
#include <string>
#include <sstream>
struct Product_Type
{
std::string name;
double weight;
double price;
};
void get(Product_Type& p)
{
std::string line, s1, s2;
std::getline(std::cin, line);
std::istringstream iss(line);
std::getline(std::getline(iss, p.name, ';') >> p.weight >> s1 >> p.price, s2);
if (not iss)
std::cerr << "\nError: Problem with input\n\n";
}
int main() {
Product_Type pt;
get(pt);
std::cout << pt.name << " --> " << pt.weight << " --> " << pt.price << '\n';
return 0;
}
All the above is very simplified, but may give you a starting point for better ideas.
Related
I have created a .txt file which a sample would be like this:
Item 1 $1.00
Item # $2.00
Item & $3.50
Item ( $0.30
If I want to ask the user to input the item they want and find the price, how can I do that?
I have tried using .find() to solve the problem, but it didn't work because array cannot use .find().
I'm new to C++, please explain.
#include <array>
#include <string>
#include <fstream>
using namespace std;
string product;
ifstream file("Item.txt");
int main(){
cout << "Enter item you want" << endl;
cin >> product;
file.open("Item.txt", fstream::ate);
findItem.find((file.begin(),file.end(), product) + 1);
cout << findItem << endl;
file.close();
}
Here's a sample code fragment to search your file:
std::string text_line;
while (std::getline(file, text_line))
{
std::string text_item;
std::string item_name;
char dollar_sign;
double price;
std::istringstream text_stream(text_line);
text_stream >> text_item >> item_name >> dollar_sign >> price;
if (product == item_name)
{
std::cout << "Found item\n";
break;
}
}
The usual method is to create a class that models the input line, then overload operator>> to read from a stream.
Notes: 1) commas separating fields would make reading easier; 2) Remove the dollar sign to make reading easier.
So I'm trying to read input like this from the standard input (using cin):
Adam English 85
Charlie Math 76
Erica History 82
Richard Science 90
My goal is to eventually store each data piece in its own cell in a data structure I have created, so basically I want to parse the input so each piece of data is individual. Since each row of input is inputted by the user one at a time, each time I get an entire row of input that I need to parse. Currently I am trying something like this:
stringstream ss;
getline(cin, ss);
string name;
string course;
string grade;
ss >> name >> course >> grade;
The error I am having is that XCode is telling me there's no matching function call to getline which is confusing me. I have included the string library, so I'm guessing the error has to do with using getline to read in from cin to a stringstream? Any help here would be appreciated.
You are almost there, the error is most probably1 caused because you are trying to call getline with second parameter stringstream, just make a slight modification and store the data within the std::cin in a string first and then used it to initialize a stringstream, from which you can extract the input:
// read input
string input;
getline(cin, input);
// initialize string stream
stringstream ss(input);
// extract input
string name;
string course;
string grade;
ss >> name >> course >> grade;
1. Assuming you have included:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
You cannot std::getline() a std::stringstream; only a std::string. Read as a string, then use a stringstream to parse it.
struct Student
{
string name;
string course;
unsigned grade;
};
vector <Student> students;
string s;
while (getline( cin, s ))
{
istringstream ss(s);
Student student;
if (ss >> student.name >> student.course >> student.grade)
students.emplace_back( student );
}
Hope this helps.
You can just use cin >> name >> course >> grade; because >> will read until whitespace anyway.
Either you don't have a using namespace std in your code or you're not fully qualifying calls made to the API's in the std namespace with an std:: prefix, for example, std::getline(). The solution below parses CSV instead to tokenize values that have whitespace in them. The logic for stdin extraction, parsing the CSV, and converting grade from string to int are all separated. The regex_token_iterator usage is probably the most complicated part, but it uses pretty simple regex for the most part.
// foo.txt:
// Adam,English,85
// Charlie,Math,76
// Erica,History,82
// Richard,Science,90
// John,Foo Science,89
// after compiling to a.exe, run with:
// $ ./a.exe < foo.txt
// output
// name: Adam, course: English, grade: 85
// name: Charlie, course: Math, grade: 76
// name: Erica, course: History, grade: 82
// name: Richard, course: Science, grade: 90
// name: John, course: Foo Science, grade: 89
#include <iostream>
#include <sstream>
#include <regex>
#include <vector>
using namespace std;
typedef unsigned int uint;
uint stoui(const string &v) {
uint i;
stringstream ss;
ss << v;
ss >> i;
return i;
}
string strip(const string &s) {
regex strip_pat("^\\s*(.*?)\\s*$");
return regex_replace(s, strip_pat, "$1");
}
vector<string> parse_csv(string &line) {
vector<string> values;
regex csv_pat(",");
regex_token_iterator<string::iterator> end;
regex_token_iterator<string::iterator> itr(
line.begin(), line.end(), csv_pat, -1);
while (itr != end)
values.push_back(strip(*itr++));
return values;
}
struct Student {
string name;
string course;
uint grade;
Student(vector<string> &data) :
name(data[0]), course(data[1]), grade(stoui(data[2])) {}
void dump_info() {
cout << "name: " << name <<
", course: " << course <<
", grade: " << grade << endl;
}
};
int main() {
string line;
while (getline(cin, line)) {
if (!line.empty()) {
auto csv = parse_csv(line);
Student s(csv);
s.dump_info();
}
}
}
If I ask a user for an input that's separated by dashes, how can I store them into multiple variables? One for each part that is separated by the dashes.
for example, this doesn't work.
cout << "enter date" << endl;
// user inputs something like 11/11/1980
cin >> month
I understand the input is stored in the iostream but how can I store whats after the first slash
I think that you should look into using the date tools that C++11 already provides to you.
time.h's struct tm has it's own streaming operators. So I'd do something like this:
#include <iostream>
#include <sstream>
#include <iomanip>
#include <ctime>
using namespace std;
int main() {
tm t;
istringstream ss("11/11/1980");
ss >> get_time(&t, "%m/%d/%Y");
cout << t.tm_mon + 1 << ' ' << t.tm_mday << ' ' << t.tm_year << endl;
return 0;
}
I don't know why but it's noteworthy that tm.tm_mon is 0-based...
See also: http://en.cppreference.com/w/cpp/io/manip/put_time
I just got into the same situation, and I found this works.
Hope this is still useful.
#include <iostream>
#include <string>
#include <iomanip>
int main()
{
// This is to initialize variables:
std::string mm,dd,yy;
// Now get the input
std::cout << "Enter the Date number: ";
// Then split it using std::getline() with delimiter '/'
// and assign them to relevant variables
std::getline(std::cin, mm,'/');
std::getline(std::cin, dd, '/');
std::cin >> yy;
std::cout << mm << " " << dd << " " << yy;
}
you can store input in string (or char array) and then later separate you desired parts, for that purpose you can create a function which will take the input string as parameter and separate its parts.
You could search the web for "c++ read date" for some examples.
One method is:
unsigned int month;
unsigned int day;
unsigned int year;
char separator;
cin >> month >> separator >> day >> separator >> year;
But there are issues with that, such as having a space before the '/'.
Another method is to fscanf with a decent format string:
fscanf(stdin, "%2d/%2d/%4d", &month, &day, &year);
but, that doesn't use the C++ streams.
You could use strings and getline:
std::string month_str;
std::string day_str;
std::string year_string;
std::getline(cin, month_str, '/');
std::getline(cin, day_str, '/');
std::getline(cin, year_str); // What's the delimiter here???
It's been a long time since I've used C++, been using Javascript in my free time and now I'm not sure exactly what I remember.
Basically I just need to separate a string into parts by looking at the spaces.
All the links I've seen are homemade functions, but I could have sworn there was a way to do it using the standard library by using streams, but again, I'm having a tough time recalling it and my google results aren't helping either.
Keep in mind that it isn't a stream I am taking from, it's just a string like "Bob Accountant 65 retired" and I have to extract each item in the string to its own data field. I've been messing with ifstreams and ofstreams but I'm not even sure what I'm doing, having forgot the syntax for it.
std::strtok is the C-style way to do it. You might be thinking of using a std::stringstream, e.g.:
#include <sstream>
#include <string>
#include <iostream>
int main() {
std::string input = "foo bar baz quxx\nducks";
std::stringstream ss(input);
std::string word;
while (ss >> word) {
std::cout << word << '\n';
}
}
When run, that displays:
foo
bar
baz
quxx
ducks
If you want to read data from a std::stringstream (or any type of std::istream, really) into a specific data type, you can follow #JerryCoffin's excellent suggestion of overloading the stream operator>> for your data type:
#include <sstream>
#include <string>
#include <iostream>
struct Employee {
std::string name;
std::string title;
int age;
std::string status;
};
std::istream& operator>>(std::istream &is, Employee &e) {
return is >> e.name >> e.title >> e.age >> e.status;
}
int main() {
std::string input = "Bob Accountant 65 retired";
std::stringstream ss(input);
Employee e;
ss >> e;
std::cout << "Name: " << e.name
<< " Title: " << e.title
<< " Age: " << e.age
<< " Status: " << e.status
<< '\n';
}
You can do it without an explicit loop like this:
string s = "Bob Accountant 65 retired";
vector<string> vs;
istringstream iss(s);
copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(vs));
std::copy reads everything from the string stream created on the third line, and pushes it into the vector created on the second line.
Here is a demo on ideone.
From the looks of things, you're trying to read a logical record from a string. For that, I'd do something like this:
struct record {
std::string name;
std::string position;
int age;
std::string status;
};
std::istream &operator>>(std::istream &is, record &r) {
return i >> r.name >> r.position >> r.age >> r.status;
}
This lets you read data from a stringstream to a record of the specified fields. It'll also, incidentally, let you read record objects from other types of streams as well (e.g., from a file using an fstream).
This is my problem: I read some lines from a txt. This txt is like this:
Ciao: 2000
Kulo: 5000
Aereo: 7000
ecc. I have to assign every word before(':') to a string and then to a map; and the numbers to a int and then to a map. The problem is that beginning from the second line, my string become ("\nKulo") ecc! I don't want this! What can I do?
This is the code:
#include <iostream>
#include <fstream>
#include <string>
#include <map>
using namespace std;
int main()
{
map <string, int> record;
string nome, input;
int valore;
ifstream file("punteggi.txt");
while (file.good()) {
getline(file, nome, ':');
// nome.erase(0,2); //Elimina lo spazio iniziale
file >> valore;
record[nome] = valore;
cout << nome;
}
file.close();
cout << "\nNome: ";
cin >> input;
cout << input << ": " << record[input] << "\n";
cout << "\n\n";
return 0;
}
The issue you have is that std::getline() is an unformatted input function and as such doesn't skip leading whitespace. From the looks of it, you want to skip leading whitespace:
while (std::getline(in >> std::ws, nome, ':') >> valore) {
...
}
Alternatively, if there are leading spaces, you can ignore() all characters up to the end of line after reading a value.
BTW, since I saw someone over here recommending the use of std::endl: do not use std::endl unless you really intend to flush the buffer. It is a frequent major performance problem when writing files.
Use the standard line reading idiom:
for (std::string line; std::getline(file, line); )
{
std::string key;
int n;
std::istringstream iss(line);
if (!(iss >> key >> n) || key.back() != ':') { /* format error */ }
m.insert(std::make_pair(std::string(key.cbegin(), std::prev(key.cend()),
n));
}
(Instead of the temporary string-from-iterators, you can also use key.substr(0, key.length() - 1), although I imagine that my version may be a bit more efficient. Or add a key.pop_back(); before inserting the data into the map.)