A real solution to the 'cin' and 'getline' issue - c++

How do I get rid of the leading ' ' and '\n' symbols when I'm not sure I'll get a cin, before the getline?
Example:
int a;
char s[1001];
if(rand() == 1){
cin >> a;
}
cin.getline(s);
If I put a cin.ignore() before the getline, I may lose the first symbol of the string, so is my only option to put it after every use of 'cin >>' ? Because that's not very efficient way to do it when you are working on a big project.
Is there a better way than this:
int a;
string s;
if(rand() == 1){
cin >> a;
}
do getline(cin, s); while(s == "");

Like this:
std::string line, maybe_an_int;
if (rand() == 1)
{
if (!(std::getline(std::cin, maybe_an_int))
{
std::exit(EXIT_FAILURE);
}
}
if (!(std::getline(std::cin, line))
{
std::exit(EXIT_FAILURE);
}
int a = std::stoi(maybe_an_int); // this may throw an exception
You can parse the string maybe_an_int in several different ways. You could also use std::strtol, or a string stream (under the same condition as the first if block):
std::istringstream iss(maybe_an_int);
int a;
if (!(iss >> a >> std::ws) || iss.get() != EOF)
{
std::exit(EXIT_FAILURE);
}
You could of course handle parsing errors more gracefully, e.g. by running the entire thing in a loop until the user inputs valid data.

Both the space character and the newline character are classified as whitespace by standard IOStreams. If you are mixing formatted I/O with unformatted I/O and you need to clear the stream of residual whitespace, use the std::ws manipulator:
if (std::getline(std::cin >> std::ws, s) {
}

Related

C++ String stream ignore() not working

I was solving a question on hackerrank and came across this problem involving string streams.
https://www.hackerrank.com/challenges/c-tutorial-stringstream/problem
For Extracting data, hackerrank has given an example:
stringstream ss("23,4,56");
char ch;
int a, b, c;
ss >> a >> ch >> b >> ch >> c; // a = 23, b = 4, c = 56
However, when I try to export it to a vector, I have to escape the ',' using:
stringstream ss(str);
vector<int> vect;
int i;
while (ss >> i)
{
vect.push_back(i);
if (ss.peek() == ',')
ss.ignore();
}
Why can't I use the extraction operation to get the required word here? Shouldn't the stream escape the ','(Sorry for the noob-level question)?
operator>> extracts the next delimited token, only so far as characters actually belong to the requested data type. So, when using operator>> to read an int, it will extract only digits, not letters, punctuation, etc. That means a comma following a number has to be read separately.
In the first example:
ss >> a reads the first int in the stream
then >> ch reads the comma after it
then >> b reads the next int
then >> ch reads the comma after it
then >> c reads the next int
In the second example:
ss >> i reads the next int in the stream, breaking the loop if fails or EOF
then ss.peek() checks if a comma exists (since the last int doesn't have one), and if found then ss.ignore() skips past it
goto #1
If you try to use operator>> to read a comma that doesn't exist, it will set the stream's eofbit state and fail the extraction. If you use while (ss >> i >> ch), the while would evaluate as false when the last int is reached. Even though ss >> i would succeed, >> ch would fail, and thus i would not be added to the vector.
In theory, you could replace if (ss.peek() == ',') ss.ignore(); inside the loop with char ch; ss >> ch instead. The end effect would be the same, at least for a string like "23,4,56". But, let's say you were given something like "23 4 56" instead. The first example would fail to handle that correctly, but the second example would handle it just fine when using peek()+ignore(), but not when using ss >> ch.
I think you can use this code to escape the ','
std::string valstr;
while (std::getline(ss, valstr, ','))
{
vect.push_back(std::stoi(valstr));
}

Stringstream. Detect end of line

Is there way to detect end of line in stringstream?
My file:
1/2
2/3
3/4
4/5
Something like that is not working:
stringstream buffer;
buffer << file.rdbuf();
string str;
getline(buffer, str);
...
istringstream ss(str);
int num;
ss >> num;
if (ss.peek() == '/') //WORKS AS EXPECTED!
{...}
if(ss.peek() == '\n') //NOT WORKING! SKIPS THIS CONDITION.
{...}
This is was warned:
if(ss.telg() == -1) //WARNED!
~~~~~
{...}
std::istringstream has an eof() method:
Returns true if the associated stream has reached end-of-file. Specifically, returns true if eofbit is set in rdstate().
string str;
istringstream ss(str);
int num;
ss >> num;
if (ss.eof()) {...}
You could always use find_first_of:
std::string str_contents = buffer.str();
if(str_contents.find_first_of('\n') != std::string::npos) {
//contains EOL
}
find_first_of('\n') returns the first instance of the EOL character. If there are none, then it returns (a very large index) std::string::npos. If you know that there is a EOL character in your string, you can get the the first line using
std::string str;
std::getline(buffer, str);
Also see NathanOliver's Answer

input data from a file when each is a different type and a comma delimiter

I need to read from file a series of information that is separated by commas
example
Orionis, 33000, 30000, 18, 5.9
Spica, 22000, 8300, 10.5, 5.1
i'm having a hard time figuring out the getline structure to make this work. The CS tutor, in the lab, says to use a getline for this but i can't seem to make it work (visual studio doesn't recognize getline in this function)
#include <iostream>
#include <fstream>
#include "star.h"
#include <string>
using namespace std;
char getChoice();
void processSelection(char choice);
void processA();
(skipping crap you don't need)
static char filePath[ENTRY_SZ];
void processA() {
ifstream openFile;
long temp, test;
double lum, mass, rad;
char name;
cout << "Please enter the full file path" << endl;
cin >> filePath;
openFile.open(filePath, ios::in);
if (openFile.good() != true) {
cout << "this file path was invalid";
}
while (openFile.good())
{
star *n = new star;
// getline(openFile, name, ',');
star(name);
getline(openFile, temp, ',');
n->setTemperature(temp);
getline(openFile, lum, ',');
n->setLuminosity(lum);
getline(openFile, mass, ',');
n->setMass(mass);
cin >> rad;
n->setRadius(rad);
}
}
From what i'm reading online (including older posts) and what my CS tutor says this should work so any help will be appreciated.
The suggestion to use std::getline() is likely implying that you'd first read a std::string and then deal with the content of this std::string, e.g., using std::istringstream.
I'd suggest not to use std::getline() and, of course, to also check inputs after they are read. To deal with the comma separator after non-std::string fields I'd use a custom manipulator:
std::istream& comma(std::istream& in) {
if ((in >> std::ws).peek() == ',') {
in.ignore();
}
else {
in.setstate(std::ios_base::failbit);
}
return in;
}
This manipulator skips leading whitespace (using the manipulator std::ws) and then simply checks if the next character is a comma. If so, the comma is extracted, otherwise the stream is set into failure mode and further attempts to read will fail until the failure state is dealt with (e.g., by using in.clear() and probably getting rid of any offending characters).
With this manipulator it is easy to read the respective values. Note, that when switching from formatted to unformatted input it is likely necessary that leading whitespace (e.g., in this case line breaks) need to be ignored. Also, the code below first attempts to read the values and uses them only when this attempt was successful: input shall always be checked after a read attempt was made, not before!
// ...
long temp;
double lum, mass, rad;
std::string name;
while (std::getline(in >> std::ws, name, ',')
>> temp >> comma
>> lum >> comma
>> mass >> comma
>> rad) {
// use the thus read values
}

Issues with characters at the end of an istream

I'm writing a parser, and I was previously having trouble when I try to parse identifiers (anything that's valid for a C++ variable name) and unclosed string literals (anything starting with ", but missing the closing ") at the end of my input. I think it's because the lexer (TokenStream) uses std::noskipws in these cases and builds the token character by character. Here is where I believe I have narrowed down the problem (shown only for one of the two cases, as the other is very similar logic):
std::string TokenStream::get()
{
char c;
(*input) >> c; // input is of type istream*
// other cases...
if (c == '"')
{
std::string s = stringFromChar(c); // just makes a string from the char.
char d;
while (true) // 1)
{
(*input) >> std::noskipws >> d;
std::cout << d; // 2)
if (d == '"')
{
s += d;
(*input) >> std::skipws;
break;
}
s += d;
}
return s;
}
// other cases...
}
Note that this function is supposed to just generate tokens from the input in a stream-like fashion. Now, if I input either a literal (like asdf) or an unclosed string (like "asdf), then the program will hang, and the line marked 2) will just output the last character of the input (in my examples, f) over and over again forever.
I've solved this problem by using a check for input->eof(), but my question is this:
Why does the loop (marked 1) in comments) keep executing when I hit the end of stream, and why does it just print that last character read every time through the loop?
Lets look at the loop in question line-by-line
while (true) // 1)
That's gonna loop, unless a break is encountered
{
(*input) >> std::noskipws >> d;
Read a character. If can't read character, d is likely to be unchanged.
std::cout << d; // 2)
Print the character that is just read
if (d == '"')
Nope, the last character was not " (as specified in the question)
{
s += d;
(*input) >> std::skipws;
break;
}
s += d;
}
Therefore the break is never encountered and the last character is printed in an endless loop.
Fix: always use a while look like this for input:
char ch;
while (input >> ch) {
// ch contains a new letter, deal with it
}

How can I include '\n' into the while (cin >> x) idiom?

My understanding is that the operator >> returns the left operand, so cin >> x returns cin which has a value of false if we encounter an EOF or other errors. Now my guess is that since cin skips whitespace, characters like \n don't get stored. What I'd like to do is something like this:
int x;
while((cin >> x) and x != '\n')
// do stuff
So that when cin encounters a newline in the stream, it exits the input loop (as it would normally do with say, EOF).
Using >> operator directly won't work, because as you say it skips whitespace including newlines.
What you can do is to read a single line using std::getline, and then you can read all input from the line with std::stringstream.
std::string line;
if (std::getline(std::cin, line)) {
std::istringstream ss(line);
int x;
while (ss >> x) {
//....
}
}