Home > Net >  Why does std::cin fail to read my input properly?
Why does std::cin fail to read my input properly?

Time:10-18

I was working on an assignment for my C class with the code below:

#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

string fileName;
string cheek,s;
long count;
ifstream input;
bool ask=1;

int main(){
  while(ask){
    cout<<"Enter a filename :\n";
    cin>>fileName;
    cout<<"reading file...";
    input.open(fileName);
    if(input){
      ask=0;
    }
  }
  istringstream iss;
  cout<<"dumping contents...";
  while(getline(input,cheek)){
    //
  }
  iss.str(cheek);
  cout<<"parsing...";
  while(iss.peek()!=EOF){
    s=iss.peek();
    if(s=="A"||s=="E"||s=="I"||s=="O"||s=="U"||s=="a"||s=="e"||s=="i"||s=="o"||s=="u"){
      count  ;
    }
  }
  cout<<"\nCOUNT="<<count<<"\nresetting...\n";
  count=0;
  iss.str("");
  input.close();
  fileName="";
  cheek="";
  s="";
}

For some reason, cin does not recognize when I am clearly pressing the Enter key. Is there a workaround for this?

CodePudding user response:

 while(getline(input,cheek)){

This while loop will continue to run as long as the while loop condition evaluates to a logical true. Each time getline() gets called, the next line from the file gets read, and getline() returns true. After the end of the file gets reached getline() finally returns false.

Nothing appears to happen in the loop itself:

    //
  }

That's it. That's the entire loop. So, in your own words, can you describe what happened in the loop? The only answer you can give: the entire file has been read and its contents thrown away, one line at a time, after being stored in the cheek variable. Since the very first thing that std::getline does is erase the string it gets as a parameter, at the conclusion of this whole process you will always end up with an empty cheek, and the entire file has been completely read.

After that loop there's some code that seems to assume that there's something in cheek that's worth exploring, but, for the reasons outlined above, there isn't anything there.

And that's why the shown code accomplishes nothing. It looks like you want the subsequent logic to be placed inside the while loop, instead of after it. To do that, all you have to do is follow The Golden Rule Of Computer Programming: "Your computer always does exactly what you tell it to do, instead of what you want it to do". According to this rule, all you have to do is to move the appropriate parts of the logic into the while loop, itself.

CodePudding user response:

In addition to the issue addressed in Sam's answer, you have a number of other issues. Avoid the use of global variables. Instead, declare all variables within the scope needed. For example, you can include all values in main() and some within your while (getline ...) loop, e.g.

#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main(){
  string fileName;
  string cheek;
  long count = 0,       /* count must be initialized */
       line = 0;
  ifstream input;

As mentioned in the comment, your ask variable is superfluous. It isn't wrong, but you can simply accomplish the same thing by looping continually and then break the read-loop when good input is received, e.g.

  while (true) {
    cout << "Enter a filename : ";
    cin >> fileName;
    cout << "reading file...\n";
    input.open(fileName);
    if (input){
      break;
    }
  }

Your file-read loop is quite awkward. Your loop condition is correct, but it would be better to simply check the characters in each line. You can use istringstream, but instead of using peek(), simply extract a character and then check the character against your list of vowels, e.g.

  cout << "dumping contents...\n";
  
  while (getline(input, cheek)) {
    char c;
    istringstream iss (cheek);
    cout << "parsing line " << (line  ) << '\n';
    while (iss >> c) {
      if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' || 
          c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
          count  ;
      }
    }
  }

You don't actually "dump" any of the file content. If you do want to dump each line of input, add:

    cout << input << '\n';

within your file-read loop before cout << "parsing line ....

Note the addition of a line counter allowing you to also report the number of lines checked as well as the number of vowels found. The line counter can be used as a simple debug tool to confirm the parsing of each line and then removed when no longer needed. The final output can be:

  cout << "\nLINES = " << line << "  COUNT = " << count << '\n';
}

There is no reason to close the file or reset the variables as you do not loop to read from another file.

Example Input File

$ cat dat/fleas2line.txt
my dog has fleas
my cat has none

Example Use/Output

$ ./bin/dumpfile
Enter a filename : dat/fleas2line.txt
reading file...
dumping contents...
parsing line 0
parsing line 1

LINES = 2  COUNT = 8

While it matters little difference in this short assignment, make sure you read and understand Why is “using namespace std;” considered bad practice?

Let me know if you have further questions. The complete source for your convenience is:

#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

int main(){
  string fileName;
  string cheek;
  long count = 0,       /* count must be initialized */
       line = 0;
  ifstream input;
  
  while (true) {
    cout << "Enter a filename : ";
    cin >> fileName;
    cout << "reading file...\n";
    input.open(fileName);
    if (input){
      break;
    }
  }
  
  cout << "dumping contents...\n";
  
  while (getline(input, cheek)) {
    char c;
    istringstream iss (cheek);
    cout << "parsing line " << (line  ) << '\n';
    while (iss >> c) {
      if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' || 
          c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
          count  ;
      }
    }
  }
  
  cout << "\nLINES = " << line << "  COUNT = " << count << '\n';
}
  •  Tags:  
  • c
  • Related