CSC 115.005/006 Sonoma State University Spring 2022
Scribbler 2
CSC 115.005/006:
Programming I
Scribbler 2
Instructor: Henry M. Walker

Lecturer, Sonoma State University
Professor Emeritus of Computer Science and Mathematics, Grinnell College


Course Home References Course Details: Syllabus, Schedule, Deadlines, Topic organization MyroC Documentation Project Scope/
Acknowledgments

Notes:

Character-by-Character Input

As noted in the previous session on using scanf for input, processing of user input often involves two steps:

  1. read data as a sequence of characters

  2. process the character sequence as needed (e.g., converting to a number)

C's scanf function combines these two steps and is useful in many contexts. However, sometimes processing in a program may allow varying types of input — for example, the next element to be read may be a number or a word. In such cases, a programmer may need to separate these two input steps.

This reading explores character-by-character input in three parts:


Reading Characters

For some applications, a program reads one character and subsequent processing may depend upon the character read.

C provides three approaches for reading individual characters.

Data conversion

Some references declare int variables and sets them to char values, while the examples given here declare char variables set to char values. The local system automatically converts the int values that getchar and getc into char values when set to a char variable, so on the local system, these declarations are valid. Just be aware that some systems may not support this automatic conversion.


Example 1: A simple option-based program

Consider a program in which a user enters integers and real numbers, and the program computes sums in each category. In particular,

In this application, user commands and input are on the same line, so a program must read an option and then decide what to do next.


Two versions of this program follow, illustrating alternatives in character processing.

Version 1: using getchar

The following program is available as option-prog-1.c.

/* program to read integers and real numbers 
   and to compute sums in each category.  
   if a user types 'i', an integer should follow
      on the same line
   if a user types 'r', a real number should follow
      on the same line
   if the user types 'q', the program should report
      the integer and real sums and quit

   version a:  processing the user option with getchar
*/

#include <stdio.h>
#include <ctype.h>

int main ()
{
  /* variable declaration and initialization */
  int int_sum = 0;
  double real_sum = 0.0;
  char ch;
  int i;
  double d;

  printf ("program to compute integer and real sums\n");
  printf ("user options:\n");
  printf ("  enter i and an integer on the same line\n");
  printf ("  enter r and a real number on the line\n");
  printf ("  enter q to quit\n");

Challenges in this application arise when processing the end of each line of user input. In particular, after the user enters an 'i' or 'r' option, the user will type a number. White space might follow the number on the same line, and then the user will type a newline character '\n'.


/* strip initial white space */
  while (isspace (ch = getchar ()));

  /* allow both uppercase and lowercase options */
  ch = tolower (ch);

C allows an assignment statement within a call to a function. Here,


  /* process line */
  while (ch != 'q')
    {      
      if (ch == 'i')
        {
          scanf ("%i", &i);
          int_sum += i;
        }
      else if (ch == 'r')
        {
          scanf ("%lf", &d);
          real_sum += d;
        }
      else
        {
          printf ("invalid option:  %c\n", ch);
        }
      

After the user option is identified, the program reads and processes an integer or a real number, as directed.


      /* strip newline and any other white space */
      while (isspace (ch = getchar ()));
      
      /* allow both uppercase and lowercase options */
      ch = tolower (ch);
    }

  printf ("totals:\n");
  printf ("   integer sum: %d\n", int_sum);
  printf ("   real sum:    %lf\n", real_sum);

  return 0;
}

Once the user types 'q', the main loop exists, and results are printed.


Version 2: using scanf with white space in format string

Normally, the %c option in a scanf does not skip white space. However, as noted on the session on using scanf for input, white space within a format string directs the computer to skip over any amount of white space until a non-white-space character is encountered. With this observation, the lines

  /* strip initial white space */
  while (isspace (ch = getchar ()));

in version 1 of the program might be replaced with

  /* strip initial white space */
  scanf (" %c", &ch);

The revised program is available as option-prog-2.c.


Reading Strings or Lines of Input

C also provides at least four approaches for reading strings of characters. Each function has its own special characteristics.

In what follows, each approach includes an example. The sample code utilizes the following macro and declaration

#define MaxLen 10
char str [MaxLen];  

These declarations allow room for 10 characters in the str array, including the null character at the end.


Notes





Of the approaches identified, the warnings given indicate:

Altogether, in reading strings, consider using one of the following approaches:


Processing Strings

Once a string is read, processing can retrieve desired information. As with everything else in this reading, C provides at least two approaches.

Use the online manual for details on each of these functions:

man sscanf
man atoi
man atof

To illustrate these two basic approaches, consider the problem of reading from the terminal the 2-letter abbreviation for a state and its population. For the purposes of illustration, assume the first 21 characters on a line will give the state's 2-letter abbreviation and the characters following on the line will specify the current population. For example, any of the following lines might be encountered as input:

IA                    3107000
NH                    1330608
NC                   10042802
TN                    6512027

In this example, the 21-character field width for states provides substantial room for a state abbreviation, many of those characters (e.g. 19 or so characters) are spaces.)

Assuming no line would exceed 100 characters, a program might read the entire line of input with fgets:

char line [101];   /* allow room for the terminating NULL */
fgets (line, 101, stdin);

sscanf approach

Since the problem specifies a 2-letter abbreviation for each state, the desired data can be stored in a small char array together with an integer variable. With this set up, the sscanf then reads from the line in much the same way that scanf reads from the terminal:

char state [3];
int pop;
sscanf (line, "%2s %d", state, &pop);

Using atoi (and/or atof)

Now suppose the 2-letter state abbreviation is replaced in the input by the full state name:

Iowa                  3107000
New Hampshire         1330608
North Carolina       10042802
Tennessee             6512027

As this data highlights, a state name may include one or two words. Thus, reading using scanf with a format string "%21s" is not adequate. The "21" width will restrict input to the specified 21 characters. However, "%s" reads just one word, and there is no way to know whether the state being read will have one word or two.


Since state names are guaranteed to fall within the first 21 characters, processing could copy the state and then focus on the remaining characters.

char state [22];
int pop;

/* copy first 21 characters, with NULL at end */
strncpy (state, line, 21);
state [21] = '\0';

/* convert the string characters,
   starting at position 21, to an integer */
pop = atoi (line + 21);


created 10 April 2008 by Henry Walker
revised 6 March 2010 by Henry Walker
revised 5 August 2011 by April O'Neill
revised 28 October 2011 by Dilan Ustek
minor editing 29 October 2011 by Dilan Ustek
revised, expanded, and reformatted 1-2 June 2016 by Henry Walker
Valid HTML 4.01! Valid CSS!
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu.