Supplementary Lecture Notes - cin.fail(), cin.clear(), cin.eof() and strchr()

James S. Plank


1. cin.fail() and cin.eof()

Part of C++ input processing is the function cin.fail(). This returns non-zero (true) if the last cin command failed, and zero (false) otherwise. When you reach the end of a file and try to read anything, this is a classic time for cin.fail(). See the program cinfail.cpp:


  #include <iostream>
  using namespace std;

  int main()
  {
    string s;
    int i;

    i = 0;
    while (1) {
      i++;
      cin >> s;
      if (cin.fail()) return 0;
      cout << "String " << i << ": " << s << endl;  
    }
}

You'll note, when you run this, it ends when the end of the file has been reached, because cin.fail() returns true here:

  UNIX> g++ cinfail.cpp
  UNIX> cat input-1.txt
  Cats foot iron claw
  Neuro-surgeons scream for more
  At paranoias poison door.
  21st century schizoid man.
  UNIX> a.out < input-1.txt
  String 1: Cats
  String 2: foot
  String 3: iron
  String 4: claw
  String 5: Neuro-surgeons
  String 6: scream
  String 7: for
  String 8: more
  String 9: At
  String 10: paranoias
  String 11: poison
  String 12: door.
  String 13: 21st
  String 14: century
  String 15: schizoid
  String 16: man.
  UNIX> 
cin.fail() also returns 1 if you try to read an integer and it receives something that cannot be converted to an integer. For example, see cinfail2.cpp:


  #include <iostream>
  using namespace std;

  int main()
  {
    int j;
    int i;

    i = 0;
    while (1) {
      i++;
      cin >> j;
      if (cin.fail()) return 0;
      cout << "Integer " << i << ": " << j << endl;  
    }
  }

When we run it on input-2.txt, you see that it quits after reading the seventh word, because that is not an integer:

  UNIX> g++ cinfail2.cpp
  UNIX> cat input-2.txt
  30 40 50 60 70 -100 Fred 99 88 77 66
  UNIX> a.out < input-2.txt
  Integer 1: 30
  Integer 2: 40
  Integer 3: 50
  Integer 4: 60
  Integer 5: 70
  Integer 6: -100
  UNIX> 
So, how do you know if you read an incorrect input word, or if you are at the end of the file? This is when you use cin.eof() and cin.clear(). First, cin.eof() returns 1 if you tried to read something but were at the end of file. Second, cin.clear() is used to "clear the error state" of cin. In other words, when an input failure occurs and cin.fail() returns true, the input buffer (cin) is placed in an "error state", and further input processing will not work unless you clear the state by calling cin.clear(). Used together with cin.fail(), cin.eof() and cin.clear() let you process and error check a variety of input. For example, cinfail3.cpp below reads all the integers in a file and prints them out. It uses cin.fail()/cin.clear() to check for and ignore non-integers, and uses cin.eof() to discover the end of the file:


  #include <iostream>
  using namespace std;

  int main()
  {
    int j;
    int i;
    string dummy;

    i = 0;
    while (1) {
      cin >> j;
      if (!cin.fail()) {
        i++;
        cout << "Integer " << i << ": " << j << endl;  
      } else if (cin.eof()) {
        return 0;
      } else {
        cin.clear();
        cin >> dummy;
      }
    }
  }

When we run it on input-3.txt, you see that it works as promised:

  UNIX> g++ cinfail3.cpp
  UNIX> cat input-3.txt
  Black 100 as 50 a 25 dark 12 night -5 she 0 was -500
  UNIX> a.out < input-3.txt
  Integer 1: 100
  Integer 2: 50
  Integer 3: 25
  Integer 4: 12
  Integer 5: -5
  Integer 6: 0
  Integer 7: -500
  UNIX> 

2. strchr() to check for one of a bunch of characters

Here's a nice trick. Suppose you want to know whether a character is one of the characters '0',  'A',  'b',  '.',  ':'  or  '/'. Well, one way to test for this is to do a bunch of if statements. The other way is to use strchr(). Remember, strchr(s, c) returns a pointer to the first instance of c in s, and it returns NULL if c is not in s. So, if you want to perform the above test, you can say:
  if (strchr("0Ab.:/", c) != NULL)
If that returns true, then c is one of the desired characters. Otherwise, it's not.

For example, suppose we want to write a program that has the user enter a card by typing the rank and the suit, like "AH" for the ace of hearts, and "2C" for the two of clubs. Then the code computes the card's rank in the deck, where 2C is card 0 and AS is card 51. Here's a way to do it (in readcard.cpp):


  #include <iostream>
  #include <cstring>
  using namespace std;

  const char *ranks = "23456789TJQKA";
  const char *suits = "CDHS";

  const char *longranks[13] =     // array of char * strings (C-style)
     { "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine",
     "Ten", "Jack", "Queen", "King", "Ace" };

  const char *longsuits[4] =     // array of char * strings (C-style)
     { "Clubs", "Diamonds", "Hearts", "Spades" };

  int main()
  {
    string s;
    const char *str;            // str will point to a C-style string
    int cnum;

    while (1) {
      cout << "Enter a card: ";
      cin >> s;
      str = s.c_str();
      if (strlen(str) != 2) {
        cout << "The card must the format 'rank-suit', such as 'AH' or '2C'\n";  
      } else if (strchr(ranks, str[0]) == NULL) {  // check rank
        cout << "The rank must be one of " << ranks << endl;  
      } else if (strchr(suits, str[1]) == NULL) {  // check suit
        cout << "The suit must be one of " << suits << endl;  
      } else {
        cnum = (strchr(suits, str[1]) - suits)*13 + 
               (strchr(ranks, str[0]) - ranks);
        cout << "Good card: #" << cnum << ": The " <<   
            longranks[cnum%13] << " of " << longsuits[cnum/13] << ".\n";  
      }
    }
  }

Note the use of pointer arithmetic to get the rank and suit. Study that one carefully. It's a good trick to know.

Here's an example of it running:

  UNIX> g++ readcard.cpp
  UNIX> a.out
  Enter a card: AH
  Good card: #38: The Ace of Hearts.
  Enter a card: 2C
  Good card: #0: The Two of Clubs.
  Enter a card: 7S
  Good card: #44: The Seven of Spades.
  Enter a card: TD
  Good card: #21: The Ten of Diamonds.
  Enter a card: YS
  The rank must be one of 23456789TJQKA
  Enter a card: TY
  The suit must be one of CDHS
  Enter a card: Fred
  The card must the format 'rank-suit', such as 'AH' or '2C'
  Enter a card: ^C
  UNIX>