1

I'm beginner to Cpp and trying to go through the examples given a book named "Programming Principles and Practice using c++" (2nd edition).

For further reference I'm talking about the "4.6.4 A text example" where a sentence needs to be taken as an input and build a dictionary of words (after sorting).

The example which was mentioned was like the below.

// simple dictionary: list of sorted words
int main()
{
  vector<string> words;
  for(string temp; cin>>temp; ) // read whitespace-separated words
     words.push_back(temp); // put into vector
  cout << "Number of words: " << words.size() << '\n';
  sort(words); // sort the words
  for (int i = 0; i<words.size(); ++i)
     if (i==0 || words[i–1]!=words[i]) // is this a new word?
        cout << words[i] << "\n";
}

In the above code sample line number 5 (in the for loop expression), the cin >> temp didn't make sense to me. Why? After running the code the console popped up and I started to enter the sentence with couple of words and even after hitting enter, I wasn't able to terminate the string and go to the next line? How to terminate inputs here?

V B
  • 75
  • 5
  • Off-topic, but -- `if (i==0 || words[i–1]!=words[i])` will not work for something like `apple banana apple banana`. The easiest solution is to use a `std::set`, since not only does it sort, it only stores unique items. – PaulMcKenzie Apr 05 '23 at 16:55
  • 4
    Currently the stream has to fail to read to exit the loop. When reading into a `std::string` this is pretty hard to do without closing the input stream. Depending on the terminal software being used that can be done with CTRL+D xor CTRL+Z on most desktop platforms. Note that once close it may be difficult to open `cin` again. – user4581301 Apr 05 '23 at 16:55
  • 1
    Typically what I do is something like [option 2 in this linked answer](https://stackoverflow.com/a/7868998/4581301). Read the whole line from the input stream, write that line into second stream. Loop through the second stream. At the end of the line, the second stream ends, exiting the loop. – user4581301 Apr 05 '23 at 16:57
  • @PaulMcKenzie: you missed `sort(words);`. – Jarod42 Apr 05 '23 at 16:58
  • Alternative would be pipe: `cat some_file | your_app` – Jarod42 Apr 05 '23 at 17:00
  • @Jarod42 Yeah, I missed it. Still, using `std::set` would eliminate the need for `sort()` or the subsequent `for` loop. – PaulMcKenzie Apr 05 '23 at 17:06
  • Minor danger: the `–` in `words[i–1]` is ***not*** a minus sign. – user4581301 Apr 05 '23 at 17:11

4 Answers4

2

the cin >> temp didn't make sense to me. Why?

  for(string temp; cin>>temp; ) // read whitespace-separated words
     words.push_back(temp); // put into vector

It's equivalent to:

string temp;
while (cin >> temp)
{
    words.push_back(temp);
}

cin>>temp will evaluate to false when the end of the input stream is reached and trigger the loop to break out. Otherwise, temp is assigned a string and cin>>temp evaluates to true.

selbie
  • 100,020
  • 15
  • 103
  • 173
1

The >> operator is an overloaded function.

What's really being called in operator>>(cin, temp).

The function returns the stream (cin) itself which have an overloaded bool conversion operator, which returns true if the stream state is fine. It returns false on failure, like for example end-of-file.

Which means the stream and the input operators can be used as part of a boolean condition to read until you reach end-of-file.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • same comment here: The info OP is missing is: What is end-of-file when they type at the console? What to enter? – 463035818_is_not_an_ai Apr 05 '23 at 17:01
  • @463035818_is_not_a_number: This example is copy pasted (nothing was changed) and the author (c++ inventor) tried to explain what happens after the example is executed. In the book, somehow it got terminated but when I try the same code, it isn't. – V B Apr 05 '23 at 17:46
1

cin >> temp will evaluate to false if the input operation fails. The most common failure when reading into a string is to reach the end-of-file, and that's the condition you want to cause.

On most Linux (or other POSIX) shells, the way to input an end-of-file marker is to press ctrl+d. In the Windows console host it's ctrl+x.

Alternatively, instead of interacting directly with the program, you can hook its input up to the output of some other command using a pipeline. When the command generating output terminates and closes its end of the pipe your program will see an end-of-file condition. For example, something like this would work on pretty much any shell:

echo "this is some text" | your-program

Or, using most modern POSIX shells, you could use a heredoc. This works basically the same as above, but the shell itself writes to your program's input stream instead of hooking up an external command to do so:

your-program <<EOF
this is some text
EOF
Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
1

As the program is currently written, the stream has to fail to read to exit the loop. When reading into a std::string this is pretty hard to do, string will gleefully eat just about anything, without closing the input stream. Depending on the terminal software being used, closing the stream can be done with CTRL+D xor CTRL+Z on most desktop platforms.

Note that once closed, it may be difficult to open cin again, so typically what I do is something like option 2 in this linked answer. Read the whole line from the input stream, write that line into second stream. Loop through the second stream. At the end of the line, the second stream ends, exiting the loop.

A trivial multiline version of the original code could look like

int main()
{
    std::string line;
    while (std::getline(cin, line))
    {
        std::istringstream iss(line); // #include <sstream>
        vector<string> words;
        for(string temp; iss>>temp; ) // read whitespace-separated words
            words.push_back(temp); // put into vector

        cout << "Number of words: " << words.size() << '\n';
        sort(words); // sort the words
        for (int i = 0; i<words.size(); ++i)
            if (i==0 || words[i-1]!=words[i]) // is this a new word?
                cout << words[i] << "\n";
    }
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • This worked, although some advanced topics like stringstreams were used, but I got a picture that the example mentioned in the book at this point of time cannot terminate a string without additional help like the code you mentioned. – V B Apr 05 '23 at 17:50