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();
}
}
}
Related
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.
I am making a bowling program for school that stored scores in a text file with the format:
paul 10 9 1 8 1, ...etc
jerry 8 1 8 1 10 ...etc
...etc
I want to read the file into a stringstream using getline() so I can use each endl as a marker for a new player's score (because the amount of numbers on a line can be variable, if you get a spare or strike on round 10). I can then read the stringstream using >> to get each score and push it into a vector individually.
However, when trying to use getline(fstream, stringstream), I get an error
no instance of overloaded function "getline" matches the argument list -- argument types are: (std::fstream, std::stringstream)
How can I make this work?
My code looks like this:
#include <iostream>
#include <vector>
#include <fstream>
#include <exception>
#include <string>
#include <sstream>
using namespace std;
//other parts of the program which probably don't matter for this error
vector <int> gameScore;
vector<string> playerName;
int i = 0;
int j = 0;
string name;
int score;
stringstream line;
while (in.good()){ //in is my fstream
playerName.push_back(name);
cout << playerName[i] << " ";
i++;
getline(in, line);
while (line >> score){
gameScore.push_back(score);
cout << gameScore[j] << " ";
j++;
}
}
You can't use std::getline() to read from a std::ifstream directly into a std::stringstream. You can only read into a std::string, which you can then assign to the std::stringstream, eg:
vector<int> gameScore;
vector<string> playerName;
string name, line;
int score;
while (getline(in, line)){
istringstream iss(line);
iss >> name;
playerName.push_back(name);
cout << name << " ";
while (iss >> score){
gameScore.push_back(score);
cout << score << " ";
}
}
I have a file that have multiple lines of the sample data given in the code. Each line is an object. I've been trying to work with string tokenizer but I keep getting errors.
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string input = "kevin hill;8;8;jacky knight;5;6;alejandro wilson;jordan walls;6;layla penn;7;mindy kaling;9;jon adams;8;";
std::istringstream ss(input);
std::string token;
std::string mN, fN, mgr, psy, physio, tCo, fCo;
int mSta, mStr, fSta, fStr, psyS, physioS, tCoS, fCoS;
struct points
{
std::string athName, sportName;
int totPoints, sc;
points(int totPoints, int sc, std::string athName, std::string sportName)
{
this->totPoints = totPoints;
this->sc = sc;
this->athName = athName;
this->sportName = sportName;
}
};
while (getline(ss, token, ';'))
{
mN >> mSta >> mStr >> fN >> fSta >> fStr >> mgr >> psy >> psyS >> physio >> physioS >> tCo >> tCoS >> fCo >> fCoS;
}
points *one = new points(mSta, mStr, mN, fN);
std::cout << one->athName << std::endl;
}
I'm getting error in the beginning of the while loop as the >> after mN is giving "no operator matches these operands" error. When I start the while loop with the istringstream ss as:
ss >> mN >> mSta >> .....
It executes, but skips the first name and reads 8;8;jacky as one string and I guess that is because it is trying to complete a string but even then it is skipping the delimiter as it stops reading at a whitespace.
I'm clueless what is happening here. How do I read different data types using delimiters and make objects with them? Any suggestions?
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.)