This question already has answers here:
Line by line reading in C and C++?
(7 answers)
Closed 8 years ago.
Im using the folowing code to read a file with first and last names in it.
firstname lastname
firstname lastname
do
{ in >> tmp2;
cout << tmp2;
} while(tmp2 != '\n');
this however doesn't detect the end of the line so I cannot progress as I get a infinite loop. Note tmp2 is a char.
how can I fix this.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream fin("file");
string first, last, comment;
while (fin >> first >> last) {
cout << first << ' ' << last << endl;
getline(fin, comment); // get the rest annoying strings
}
fin.close();
return 0;
}
One solution: use a stringstream
std::stringstream sstrm;
std::string instr;
while (std::getline(std::cin, instr)) {
sstrm.str(instr);
std::string fname, lname;
sstrm >> fname >> lname;
std::cout << fname << ' ' << lname << '\n';
}
This discard anything after the first two tokens in a line.
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.
This question already has answers here:
std::cin input with spaces?
(8 answers)
Closed 5 years ago.
I would like to know how to get rid of the '\n' for when I execute cin.get(). For example:
#include <iostream>
int main() {
std::cout << "How many pieces of c: ";
struct c{
std::string name;
int val;
};
int amount;
std::cin >> amount;
c * c1 = new c[amount];
std::cin.get();
for(int i = 0; i < amount; i++){
std::cout << "C Number " << i + 1 << ":" << std::endl;
std::cout << "Please enter the name: ";
c1[i].name = std::cin.get();
std::cout << "Please enter the val: ";
std::cin >> c1[i].val;
std::cin.get();
}
return 0;
}
I've tried using cin.get() to eliminate the '\n' thats last read after my std::cin, but my program only takes in the first name and then executes the rest of the program without letting me continue to type the rest of the name and val values. In short, I want to be able to store the rest of my name and val values. Thanks!
c1[i].name = std::cin.get();
I'm not sure why you used get() instead of operator >>, like you did two lines later. get() reads a single character - meaning, only the first character you type in is saved as name. Everything else is read (most likely unsuccessfully) into val, and then new line character is, just like you intended, digested by the final get().
For reading int, try using getline and stringstream:
std::string input(5, '\0');
int number;
std::getline(std::cin, input);
std::stringstream sstrm(input);
sstrm >> number;
For a name: You can directly use getline
std::string name(30, '\0');
std::getline(std::cin, name);
This question already has answers here:
How do I iterate over the words of a string?
(84 answers)
Closed 6 years ago.
I am reading in a file, that contains data in this format on each line. 30304 Homer Simpson I need to be able to pass this to the following constructor, the integer being the regNo, the name the rest of the string, and every student would have their own map of marks.
Student::Student (string const& name, int regNo):Person(name)
{
regNo = regNo;
map<string, float> marks;
}
I then have to add each student to a collection of students, which would be best, and how do I do this?
So far all I've got is getting the file name and checking it exists.
int main()
{
//Get file names
string studentsFile, resultsFile, line;
cout << "Enter the Students file: ";
getline(cin, studentsFile);
cout << "Enter the results file: ";
getline(cin, resultsFile);
//Check for students file
ifstream students_stream(studentsFile);
if (!students_stream) {
cout << "Unable to open " << studentsFile << "\n";
return 1;
}
}
I tried using getline with 3 arguments and " " as the delimiter but that would also split the name part of the string, so I'm not sure how to do this another way.
Replace std::cin with your input file stream of course. It would be probably sane to "trim" the name result, unless you know by 100% the input is well formatted. I added only bare-minimal error state handling to somehow "survive".
Names are read also for single/three/more variants of course, as any real world application should.
#include <iostream>
#include <string>
#include <stdexcept>
int main()
{
std::string line, name;
unsigned long long regNo;
size_t nameOfs;
while (true) {
// Read full non-empty line from input stream
try {
std::getline(std::cin, line);
if (line.empty()) break;
}
catch(const std::ios_base::failure & readLineException) {
break;
}
// parse values:
// 1. unsigned long long ending with single white space as "regNo"
// 2. remaining part of string is "name"
try {
regNo = std::stoull(line, &nameOfs);
name = line.substr(nameOfs + 1);
}
catch(const std::logic_error & regNoException) {
// in case of invalid input format, just stop processing
std::cout << "Invalid regNo or name in line: [" << line << "]";
break;
}
// here values regNo + name are parsed -> insert them into some vector/etc.
std::cout << "RegNo [" << regNo << "] name [" << name << "]\n";
}
}
A regular expression could be used:
We can then select group 2 and 3 from the result.
std::vector<Student> students;
std::regex r{R"(((\d+) )(.+))"};
for(std::string line; getline(students_stream, line);) {
auto it = std::sregex_iterator(line.begin(), line.end(), r);
auto end = std::sregex_iterator();
if(it == end || it->size() != 4)
throw std::runtime_error("Could not parse line containing the following text: " + line);
for(; it != end; ++it) {
auto match = *it;
auto regNo_text = match[2].str();
auto regNo{std::stoi(regNo_text)};
auto name = match[3].str();
students.emplace_back(name, regNo);
}
}
Live demo
You can take input using getline()and read one complete line(no third argument) and then use stringstream to extract the number and the remaining string. Example of stringstream:
string s = "30304 Homer Simpson", name;
stringstream ss(s);
int num;
ss >> num; //num = 30304
getline(ss, name); //name = Homer Simpson
cout << num;
cout << name;
This question already has answers here:
How to read a complete line from the user using cin?
(4 answers)
Closed 6 years ago.
I've been working on saving to files and this was the result. The only problem is that anything after a space is ignored, (if you typed "john smith") it would print
("the last person to use this file was: john") I am using codeblocks with the GNU GCC compiler. Here is the code:
#include <iostream>
#include <cstdlib>
#include <fstream>
using namespace std;
int main()
{
string name;
ofstream saveData;
ifstream Data;
Data.open("Info.data", ios::binary);
Data >> name;
Data.close();
cout << "The last person to use the file was " << name << endl;
cout << "What is your name?" << endl;
cin >> name;
saveData.open("Info.data", ios::binary);
saveData << name;
cout << name << endl;
system("PAUSE");
saveData.close();
return 0;
}
thanks
For strings objects of ifstream (including cin) use input from beginning till the first space, and space is SPACE, TAB, and NELINE.
So, you should use getline instead cin >>
Try this:
Data.open("Info.data");
getline(Data, name);
Data.close();
and
cout << "What is your name?" << endl;
//cin >> name;
getline(cin, name);
UPDATE:
By the way, in your code
after
Data.open("Info.data", ios::binary);
you use
Data >> name;
So, stream opened in binary mode is read by >> - it is not so good.
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.)