Ignoring whitespaces/tabs/newlines with getline() - c++

I went through lot of the resources on web but still not able to get this. I didn't understand how
std::skipws works to ignore whitespaces , tabs and newlines.
Following is my simple code
vector<string> vec;
while(1){
getline(cin, s);
if( s.compare("#") == 0)
break;
else
vec.push_back(s);
}
I will enter a line of strings with newlines, whitespaces and tabs. After input I want to store strings into the vector and that will stop when "#" string is encountered. I tried with the above code but it store spaces along with the strings in the vector though it terminates after enterting "#".

The purpose of std::getline is to read an entire line, including whitespace, into a string buffer.
If you want to read tokens from a stream, skipping whitespace, then use the standard input operator >>.
std::vector<std::string> vec;
std::string s;
while(std::cin >> s && s != "#") {
vec.push_back(s);
}
Live example

std::skipws is skipping only the leading whitespace characters in any input stream. It therefore has no effect on all the whitespaces after the first non-whitespace. If you want to read whole lines with getline(cin, s) you might as well consider removing the blanks and tabs that have been read from the string before pushing it into the container like so :
while (1){
getline(cin, s);
if (s.compare("#") == 0) {
break;
}
else {
s.erase(remove_if(s.begin(), s.end(), ::isspace), s.end());
vec.push_back( s );
}
}
For a discussion on how to remove whitespaces from a string see also : Remove spaces from std::string in C++

Related

Using cin.get() in a loop to input a string

I was wondering if there is a way of using the cin.get() fuction in a loop to read a string that could be composed of more than one word.
For example
while (cin.get(chr)) // This gets stuck asking the user for input
while (cin.get(chr) && chr != '\n') // This code doesn't allow the '\n' to be read inside the loop
I want to be able to read a whole string and be able to use my chr variable to determine if the current character being read is a '\n' character.
I'm a little familiar with the getline function. But I don't know of a way to individually go through every character in the string while counting them when using the getline. I hope what I'm saying makes sense. I'm new to programming and c++.
I basically want to determine when these characters ( ' ' , '\n') happen in my string. I will use this to determine when a word ends and a new one begins in my string.
If you want to read a whole line and count the spaces in it you can use getline.
std::string s;
std::getline(std::cin, s);
//count number of spaces
auto spaces = std::count_if(s.begin(), s.end(), [](char c) {return std::isspace(c);});
std::getline will always read until it encounters an \n.
You could try the following:
using namespace std;
int main() {
char ch;
string str = "";
while (cin.get(ch) && ch != '\n')
str += ch;
cout << str;
}
and the string str would have all characters till end line.

Why doesn't this code correctly read strings until encountering a newline?

I'm trying to write a program that reads a bunch of strings from the user, then a newline, and pushes all the strings I've read onto a stack. Here's what I have so far:
stack<string> st;
string str;
while(str != "\n")
{
cin >> str;
st.push(str);
}
However, this goes into an infinite loop and doesn't stop when I read a newline. Why is this happening? How do I fix it?
By default, the stream extraction operator (the >> operator) as applied to strings will skip over all whitespace. If you type in A B C, then a newline, then D E F, then try reading strings one at a time using the stream extraction operator, you'll get the strings "A", "B", "C", "D", "E", and "F" with no whitespace and no newlines.
If you want to read a bunch of strings until you hit a newline, you can consider using std::getline to read a line of text, then use an std::istringstream to tokenize it:
#include <sstream>
/* Read a full line from the user. */
std::string line;
if (!getline(std::cin, line)) {
// Handle an error
}
/* Tokenize it. */
std::istringstream tokenizer(line);
for (std::string token; tokenizer >> token; ) {
// Do something with the string token
}
As a note - in your original code, you have a loop that generally looks like this:
string toRead;
while (allIsGoodFor(toRead)) {
cin >> toRead;
// do something with toRead;
}
This approach, in general, doesn't work because it will continue through the loop one time too many. Specifically, once you read an input that causes the condition to be false, the loop will keep processing what you've read so far. It's probably a better idea to do something like this:
while (cin >> toRead && allIsGoodFor(toRead)) {
do something with toRead;
}
Try doing
stack<string> st;
string str;
while(str!="\n")
{
cin>>str;
if(str == "\n")
{
break;
}
st.push(str);
}
And see if that works.
And if not, then try
while ((str = cin.get()) != '\n')
instead of
while(str!="\n")

How to find a string of 2 words in a file?

With the following code, I can find a string of 1 word (in this example I'm looking for "Word"):
ifstream file("file.txt");
string str;
while (file >> str){
if (str.find("Word") != string::npos){
////
}
}
But it doesn't work if I want to find, for example, "Computer screen", which is composed of two words.
Thanks
file >> str reads a parameter (in this case, a string) delimited with whitespace. If you want to read the whole line (or in any case, more than one word at once), you can use getline operator (reads the string which is delimited by newline by default).
ifstream file("file.txt");
string str;
while (std::getline (file,str)){
if (str.find("Computer screen") != string::npos){
////
}
}
If you know there are two words and what they are, all you need is this:
ifstream file("file.txt");
string str;
while (file >> str){
if (str.find("Computer") != string::npos){
file >> str;
if (str.find("screen") != string::npos) {
////
}
}
}
But more likely, you are asking to find a single string that might be two words, or three or more.
Then, can you count on the string being on a single line? In which case, #Ashalynd's solution will work. But if the string might be broken it will fail. You then need to handle that case.
If your file is "small" - i.e. can easily fit in memory, read in the whole thing, remove line breaks and search for the string.
If it is too large, read in lines as pairs.
Something like this:
std::ifstream file("file.txt");
std::string str[2];
int i = 0;
std::getline (file,str[i]);
++i;
while (std::getline (file,str[i]))
{
int next_i = (i+1)%2;
std::string pair = str[next_i] + " " + str[i];
if (pair.find("Computer screen") != std::string::npos)
{
////
}
i = next_i;
}
All this assumes that the possible white space between the words in the string is a single space or a newline. If there is a line break with more white-space of some kind (e.g. tabs, you need either to replace white-space in the search string with a regex for white-space, or implement a more complex state machine.
Also, consider whether you need to manage case, probably by converting all strings to lower case before the match.

Extracting arguments using stringstream

I want to input a phrase and extract each character of the phrase:
int main()
{
int i = 0;
string line, command;
getline(cin, line); //gets the phrase ex: hi my name is andy
stringstream lineStream(line);
lineStream>>command;
while (command[i]!=" ") //while the character isn't a whitespace
{
cout << command[i]; //print out each character
i++;
}
}
however i get the error: cant compare between pointer and integer at the while statement
As your title "Extracting arguments using stringstream" suggests:
I think you're looking for this :
getline(cin, line);
stringstream lineStream(line);
std::vector<std::string> commands; //Can use a vector to store the words
while (lineStream>>command)
{
std::cout <<command<<std::endl;
//commands.push_back(command); // Push the words in vector for later use
}
command is a string, so command[i] is a character. You can't compare characters to string literals, but you can compare them to character literals, like
command[i]!=' '
However, you're not going to get a space in your string, as the input operator >> reads space delimited "words". So you have undefined behavior as the loop will continue out of bounds of the string.
You might want two loops, one outer reading from the string stream, and one inner to get the characters from the current word. Either that, or loop over the string in line instead (which I don't recommend as there are more whitespace characters than just space). Or of course, since the "input" from the string stream already is whitespace separated, just print the string, no need to loop over the characters.
To extract all words from the string stream and into an vector of strings, you can use the following:
std::istringstream is(line);
std::vector<std::string> command_and_args;
std::copy(std::istream_iterator<std::string>(is),
std::istream_iterator<std::string>(),
std::back_inserter(command_and_args));
After the above code, the vector command_and_args contains all whitespace delimited words from the string stream, with command_and_args[0] being the command.
References: std::istream_iterator, std::back_inserter, std::copy.

c++ string manipulation reversal

I am currently doing c++ and am going through how to take in an sentence through a string and reverse the words (This is a word......word a is This etc)
I have looked at this method:
static string reverseWords(string const& instr)
{
istringstream iss(instr);
string outstr;
string word;
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
}
int main()
{
string s;
cout << "Enter sentence: ";
getline(cin, s);
string sret = reverseWords(s);
cout << reverseWords(s) << endl;
return 0;
}
I have gone through the function and kind of understand but I am a bit confused as to EXACTLY what is going on at
iss >> outstr;
while (iss >> word)
{
outstr = word + ' ' + outstr;
}
return outstr;
Can anybody explain to me the exact process that is happening that enables the words to get reversed?
Thank you very much
iss is an istringstream, and istringstreams are istreams.
As an istream, iss has the operator>>, which reads into strings from its string buffer in a whitespace delimeted manner. That is to say, it reads one whitespace separated token at a time.
So, given the string "This is a word", the first thing it would read is "This". The next thing it would read would be "is", then "a", then "word". Then it would fail. If it fails, that puts iss into a state such that, if you test it as a bool, it evaluates as false.
So the while loop will read one word at a time. If the read succeeds, then the body of the loop appends the word to the beginning of outstr. If it fails, the loop ends.
iss is a stream, and the >> is the extraction operator. If you look upon the stream as a continuous line of data, the extraction operator removes some data from this stream.
The while loop keep extracting words from the stream until it is empty (or as long as the stream is good one might say). The inside of the loop is used to add the newly extracted word to the end of the outstr
Look up information about c++ streams to learn more.
The instruction:
istringstream iss(instr);
allows instr to be parsed when the operator>> is used, separating words thourgh a whitespace character. Each time the operator >> is used it makes iss point to the next word of the phrase stored by instr.
iss >> outstr; // gets the very first word of the phrase
while (iss >> word) // loop to get the rest of the words, one by one
{
outstr = word + ' ' + outstr; // and store the most recent word before the previous one, therefore reversing the string!
}
return outstr;
So the first word retrieved in the phrase is actually stored in the last position of the output string. And then all the subsequent words read from the original string will be put before the previous word read.