cin in a while-loop - c++

#include <iostream>
using namespace std;
int main()
{
string previous;
string current;
while (cin >> current)
{
if(current == previous)
{
cout << "repeated word";
}
previous=current;
}
return 0;
}
my questions are:
When it states while(cin>>current), why is the entire entered string of text not assigned to current? I don't understand how it compares all the words individually.
How does it get a word for previous. How does it know 2 of the same words are adjacent?
EDIT: I think I just understood why. Tell me if I am wrong but I think its because the compiler stops the cin assignment at the space and that first word is assigned to current, then it compares it to the previous word, but since it is the first word, it does not have a previous word so it just assigned the first word to previous and compares the next word in the sentence until there are no more words left. I'm fairly certain this is how it works but I am going to leave this up in case anyone is ever wondering something similar.

The default behavior is to stop on whitespace when reading strings as you are doing. You can change it to read whitespace by saying std::cin >> std::noskipws.
The previous word is always assigned at the end of the loop. Given the answer to part 1, it should be clear why previous works.

If you use std::istream::operator>> with a std::string argument, it reads characters until it finds any whitespace character. This means that it will only read a single "word" if you're entering a sentence. Use std::getline(cin, current); to read an entire line (until a newline character).
You assign current to previous: previous = current. This means that after reading current, you make previous current. So the next time, before you read, you can compare previous with current to see if they're the same thing.

If you want to use string, you must use #include string or std_lib_facilities.h

Related

Searching for char from end of file using seekg() c++

I have a file and I want to only output the last line to the console.
Here's my thoughts on how I would do it. Use file.seekg(0, ios::end) to put myself at the end of file.
Then, I could create a decrement variable int decrement = -1; and use a while loop
while (joke.peek() != '\n')
{
decrement--;
}
and get the starting position for my final line (going backwards from the end).
Knowing this, joke.seekg(decrement, ios::end); should set me to the beginning of the final line, and assuming I previously declared string input;
I would think that
getline(joke, input);
cout << input << endl;
would output it to the console.
Full code
void displayLastLine(ifstream &joke)
{
string input;
int decrement = -1;
joke.clear();
joke.seekg(0, ios::end);
while (joke.peek() != '\n')
{
decrement--;
}
joke.clear();
joke.seekg(decrement, ios::end);
getline(joke, input);
cout << input << endl;
}
The problem is, when I go to call this method for the file, nothing happens. When I step through it, the decrement just keeps on subtracting one, far beyond where a '\n' would be. To give an example of a text file, it would look something like this:
junk
garbage
skip this line
This is the line that we're looking for!
joke.seekg(0, ios::end);
This positions the file at the end.
while (joke.peek() != '\n')
Well, here's problem #1. When you're at the end of the file, peek() always returns EOF.
decrement--;
You write:
When I step through it, the decrement just keeps on subtracting one,
Well, what did you expect to happen, since that's the only thing that the loop does? The only thing your for loop does is subtract 1 from decrement. So that's what happens.
This a common problem: a computer does only what you tell it to do, instead of what you want it to do.
Although this is not optimal, your missing step is that before you peek(), you need to seek() back by one character. Then, peek() shows you the character at the current cursor position. Then, seek() back by one more character, and check peek() again, and so on.
But that still will not be sufficient for you. Most text files end with a newline character. That is, a newline is the last character in the file. So, even if you add back the missing seek(), in nearly all cases, what your code will end up doing is finding the last character in the file, the final newline character.
My recommendation for you is to stop writing code for a moment, and, instead, come up with a logical process for doing what you want to do, and describe this process in plain words. Then, discuss your proposed course of action with your rubber duck. Only after your rubber duck agrees that what you propose will work, then translate your plain language description into code.
peek does not move the file pointer, it reads the character without extracting it. So, you are constantly peeking the same value (character) and ending up in an infinite loop.
What would you need to do is something like:
while (joke.peek() != '\n')
{
joke.seek(-1, ios::cur);
}
That would put the input position at the \n, using the 2nd overload of seekg.
Please note that this is not a perfect solution. You need to check for errors and boundary conditions, but it explains your observed behaviour and gives you something to start fixing your code.
Your loop is actually only decrementing "decrement" and not using it to make the next search.
while (joke.peek() != '\n')
{
joke.seekg(decrement, std::ios::end);
decrement--;
}

Understanding a C++ program [ Bjarne Stroustrup's book ]

i need your precious help for a small question!
I'm reading the Bjarne Stroustrup's book and i found this exemple:
int main()
{
string previous = " ";
string current;
while (cin >> current) {
if(previous == current)
cout << "repeated word: " << current << '\n';
previous = current;
}
return 0;
}
My question is: What does string previous = " "; do?
It initializes previous to the character space (like when you press space). But I thought in C++ it doesn't read it, something about the compiler skipping over whitespace. Why initialize it to that then?
I have tried to write like that: string previous; and the program still work properly... so? What is the differnece? Please enlighten me x)
You seam to be confused on what it means in C++ to ignore whitespace. In C++
std::string the_string = something;
is treated the same as
std::string the_string=something ;
No when you have a string literal the whitespace in the literal is not ignored as it is part of the charcters of the string. So
std::string foo = " ";
Creates a string with one space where as
std::string foo = " ";
Creates a string with 4 spaces in it.
You are right, a whitespace is something you will never get when reading input using std::cin. Therefore, a previous string is initialized with a value that could never (i.e. when reading the first word) possibly match word read into current string.
In this case previous could alsobe initalized to an empty string, because istream::operator>> skips all the whitespace and you would never get an empty like by reading from std::cin that way. However, there are other ways of using std::cin (e.g. together with getline()), which may lead to reading an empty string.
The book explains this example in every detail.
string previous = " ";
assigns a space to the string variable 'previous'.
It may still 'work', but if you were to simply press enter on the first try, the 'repeated word' message should appear.
He could just write :)
string previous;
The idea is that the operator >> can not enter an empty string if by default there is set to skip white spaces.
So any comparison current with an empty string or a string that contains white spaces will yield false.

Cannot get second while to loop properly

I'm making a function that removes elements from a string. However, I cant seem to get both of my loops to work together. The first while loop works flawlessly. I looked into it and I believe it might be because when "find_last_of" isn't found, it still returns a value (which is throwing off my loop). I haven't been able to figure out how I can fix it. Thank you.
#include <iostream>
#include <string>
using namespace std;
string foo(string word) {
string compare = "!##$";
string alphabet = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
while(word.find_first_of(compare) < word.find_first_of(alphabet)) {
int position = word.find_first_of(compare);
word = word.substr(++position);
}
while(word.find_last_of(compare) > word.find_last_of(alphabet)){
int size = word.length();
word = word.substr(0, --size);
}
return word;
}
int main() {
cout << foo("!!hi!!");
return 0;
}
I wrote it like this so compound words would not be affected. Desired result: "hi"
It's not entirely clear what you're trying to do, but how about replacing the second loop with this:
string::size_type p = word.find_last_not_of(compare);
if(p != string::npos)
word = word.substr(0, ++p);
It's not clear if you just want to trim certain characters from the front and back of word or if you want to remove every one of a certain set of characters from word no matter where they are. Based on the first sentence of your question, I'll assume you want to do the latter: remove all characters in compare from word.
A better strategy would be to more directly examine each character to see if it needs to be removed, and if so, do so, all in one pass through word. Since compare is quite short, something like this is probably good enough:
// Rewrite word by removing all characters in compare (and then erasing the
// leftover space, if any, at the end). See std::remove_if() docs.
word.erase(std::remove_if(word.begin(),
word.end(),
// Returns true if a character is to be removed.
[&](const char ch) {
return compare.find(ch) != compare.npos;
}),
word.end());
BTW, I'm not sure why there is both a compare and alphabet string in your example. It seems you would only need to define one or the other, and not both. A character is either one to keep or one to remove.

Line Breaks when reading an input file by character in C++

Ok, just to be up front, this IS homework, but it isn't due for another week, and I'm not entirely sure the final details of the assignment. Long story short, without knowing what concepts he'll introduce in class, I decided to take a crack at the assignment, but I've run into a problem. Part of what I need to do for the homework is read individual characters from an input file, and then, given the character's position within its containing word, repeat the character across the screen. The problem I'm having is, the words in the text file are single words, each on a different line in the file. Since I'm not sure we'll get to use <string> for this assignment, I was wondering if there is any way to identify the end of the line without using <string>.
Right now, I'm using a simple ifstream fin; to pull the chars out. I just can't figure out how to get it to recognize the end of one word and the beginning of another. For the sake of including code, the following is all that I've got so far. I was hoping it would display some sort of endl character, but it just prints all the words out run together style.
ifstream fin;
char charIn;
fin.open("Animals.dat");
fin >> charIn;
while(!fin.eof()){
cout << charIn;
fin >> charIn;
}
A few things I forgot to include originally:
I must process each character as it is input (my loop to print it out needs to run before I read in the next char and increase my counter). Also, the length of the words in 'Animals.dat' vary which keeps me from being able to just set a number of iterations. We also haven't covered fin.get() or .getline() so those are off limits as well.
Honestly, I can't imagine this is impossible, but given the restraints, if it is, I'm not too upset. I mostly thought it was a fun problem to sit on for a while.
Why not use an array of chars? You can try it as follow:
#define MAX_WORD_NUM 20
#define MAX_STR_LEN 40 //I think 40 is big enough to hold one word.
char words[MAX_WROD_NUM][MAX_STR_LEN];
Then you can input a word to the words.
cin >> words[i];
The >> operator ignores whitespace, so you'll never get the newline character. You can use c-strings (arrays of characters) even if the <string> class is not allowed:
ifstream fin;
char animal[64];
fin.open("Animals.dat");
while(fin >> animal) {
cout << animal << endl;
}
When reading characters from a c-string (which is what animal is above), the last character is always 0, sometimes represented '\0' or NULL. This is what you check for when iterating over characters in a word. For example:
c = animal[0];
for(int i = 1; c != 0 && i < 64; i++)
{
// do something with c
c = animal[i];
}

Using cin.get() to grab a line of text, then using it in a loop to display that line?

Ok, so I came across this code snippet in my textbook that's supposed to echo every other character a user types in. Now, I understand the every other character part, but I'm having difficulty with the use of cin.get(). I understand why the first cin.get() is there, but why is it also inside the loop? I'm guessing I'm not fully grasping the nature of input streams...
EDIT: It just clicked... I'm an idiot. Thanks for clearing that up.
char next;
int count = 0;
cout << "Enter a line of input:\n";
cin.get(next);
while (next != '\n')
{
if ((count%2) == 0)
cout << next;
count++;
cin.get(next);
}
Thanks in advance!
cin.get in this case does not "grab a line of text" as you seem to believe. cin.get in this case grabs just a single character. With cin.get you read characters the user is typing in, one by one, one after another. This is why you have cin.get in a loop.
The call to cin.get(next) that comes before the loop is only placing the first character of buffered user input into the variable 'next.'
Once inside the loop, and the character stored in 'next' has been processed (echoed if at an even index, otherwise ignored), cin.get(next) needs to be called again to retrieve the next character.
Its printing characters present at even positions
char next;
int count = 0;
cout << "Enter a line of input:\n";
cin.get(next);//gets first character (position zero) from input stream
while (next != '\n')//check whether the character is not line feed(End of the line)
{
if ((count%2) == 0)//checks if position is even
cout << next; //if yes print that letter
count++; //increments the count
cin.get(next); //gets next character from input stream
}
We require two cin.get(...)
before entering the while loop we need to know first character(position zero)
inside while loop for getting next character
but what is the use of outside cin.get(ch) what does it do
how cin.get() works inside loop
both behaviours are lookin different
so it's making confusing
there is a statement in loop to print the character got using cin.get(next) but it will not print it.... it will print all together after pressing enter key ... actually it should display the characters as we type from the keyboard but it is not actually working like that
istream& get(char &c) gets a character from the input stream.
so on the first call cin.get(next) you typed:
"hello world!"
Then future cin.get(next) will fetch h, e, l, l, etc... on every call until the there are no more characters in the input stream and that's when it will block asking the user for more input.
Streams in C++ , are buffered. Think of them as a line of letters. When you call cin.get(var) the first character in that line is removed and returned to you. So, that's how it works.
An example would help better. When the first cin.get() is executed, let's say you type in :
LISP
and then, cin.get() will return (in the var.) L and then the buffer will look like ISP... the next call will place I in the var. and the buffer will look like SP and so on...