String Input

I l @ ve RuBoard

String Input

If you want to read a string into a program, you must first set aside space to store the string and then use an input function to fetch the string.

Creating Space

The first order of business is setting up a place to put the string after it is read. As mentioned earlier, this means you need to allot enough storage to hold whatever strings you expect to read. Don't expect the computer to count the string length as it is read and then allot space for it. The computer won't (unless you write a function to do so). For example, suppose you try something like this:

 char *name; scanf("%s", name); 

It will probably get by the compiler, but when the name is read, the name will be written over data or code in your program. That's because scanf() copies information to the address given by the argument, and in this case, the argument is an uninitialized pointer; name might point anywhere . Most programmers regard this as highly humorous , but only in other people's programs.

The simplest course is to include an explicit array size in the declaration:

 char name[81]; 

Now name is the address of an allocated block of 81 bytes. Another possibility is to use the C library functions that allocate memory, and we'll touch on those in Chapter 16, "The C Preprocessor and the C Library." In this program, we used an automatic array for name . You can do that even under K&R C because you don't have to initialize the array.

After you have set aside space for the string, you can read the string. The C library supplies a trio of functions that can read strings: scanf() , gets() , and fgets() . The most commonly used one is gets() , which we discuss first.

The gets() Function

The gets() ( get string) function is very handy for interactive programs. It gets a string from your system's standard input device, normally your keyboard. Because a string has no predetermined length, gets() needs a way to know when to stop. Its method is to read characters until it reaches a newline (\n) character, which you generate by pressing the Enter key. It takes all the characters up to (but not including) the newline, tacks on a null character (\0) , and gives the string to the calling program. The newline character itself is read and discarded so that the next read begins at the start of the next line. Listing 11.4 shows a simple means of using gets() .

Listing 11.4 The name1.c program.
 /* name1.c -- reads a name */ #include <stdio.h> #define MAX 81 int main(void) {   char name[MAX];        /* allot space                  */   printf("Hi, what's your name?\n");   gets(name);            /* place string into name array */   printf("Nice name, %s.\n", name);   return 0; } 

Here is a sample run:

 Hi, what's your name?  Davina Sprocket  Nice name, Davina Sprocket. 

Listing 11.4 accepts and stores any name (including spaces) up to 80 characters. (Remember to reserve one space for the \0 in the array.) Note that you want gets() to affect something (name) in the calling function. This means you should use an address as an argument, and, of course, the name of an array is an address.

The gets() function is more sophisticated than this preceding example suggests. Look at Listing 11.5.

Listing 11.5 The name2.c program.
 /* name2.c -- reads a name */ #include <stdio.h> #define MAX 81 int main(void) {     char name[MAX];     char * ptr;     printf("Hi, what's your name?\n");     ptr = gets(name);     printf("%s? Ah! %s!\n", name, ptr);     return 0; } 

Here is a sample exchange:

 Hi, what's your name?  Tony de Tuna  Tony de Tuna? Ah! Tony de Tuna! 

The gets() function gets input in two ways:

  • It uses an address to feed the string into name .

  • The code for gets() uses the return keyword to return the address of the string, and the program assigns that address to ptr . Notice that ptr is a pointer to char . This means that gets() must return a value that is a pointer to char .

ANSI C mandates that the stdio.h header file include a function prototype for gets() . With newer implementations , then, you need not declare the function yourself as long as you remember to include that header file. However, some older versions of C require that you provide your own function declaration for gets() .

The design of a gets() function could look something like this:

 char *gets(char * s) {     ...     return(s); } 

The function header indicates that gets() returns a pointer to a char . Note that gets() returns the same pointer that was passed to it. There is but one copy of the input string; the one placed at the address passed as a function argument, so ptr in Listing 11.5 winds up pointing to the name array . The actual design is slightly more complicated because gets() has two possible returns. If everything goes well, then it returns the address of the read string, as we have said. If something goes wrong or if gets() encounters end of file, it returns a null, or zero, address. This null address is called the null pointer and is represented in stdio.h by the defined constant NULL . Therefore, gets() incorporates a bit of error-checking, making it convenient to use constructions like this:

 while (gets (name) ! = NULL) 

Such a construction enables you to both check for end of file and read a value. If end of file is encountered , nothing is read into name. This two-pronged approach is more compact than that of getchar () , which has a return value but no argument:

 while ((ch = getchar()) != EOF) 

By the way, don't confuse the null pointer with the null character. The null pointer is an address, and the null character is a type char data object with the value zero. Numerically, both can be represented by , but they differ conceptually from each other.

The fgets() Function

One weakness of gets() is that it doesn't check to see whether the input actually fits into the reserved storage area. Extra characters simply overflow into the adjoining memory. The fgets() function improves on this questionable behavior by enabling you to specify an upper limit for the number of characters to be read. Because fgets() is designed for file I/O, it's slightly more awkward than gets() for keyboard input. It differs from gets() in three respects:

  • It takes a second argument indicating the maximum number of characters to read. If this argument has the value n , fgets() reads up to n-1 characters or through the newline character, whichever comes first.

  • If fgets() reads the newline, it stores it in the string, unlike gets() , which discards it.

  • It takes a third argument indicating which file to read. To read from the keyboard, use stdin (for standard in put) as the argument; this identifier is defined in stdio.h .

Listing 11.6 modifies Listing 11.5 to use fgets() instead of gets () .

Listing 11.6 The name3.c program.
 /* name3.c -- reads a name using fgets() */ #include <stdio.h> #define MAX 81 int main(void) {     char name[MAX];     char * ptr;     printf("Hi, what's your name?\n");     ptr = fgets(name, MAX, stdin);     printf("%s? Ah! %s!\n", name, ptr);     return 0; } 

Here is some sample output, which reveals one of the awkward aspects of fgets() :

 Hi, what's your name?  Jon Dough  Jon Dough ? Ah! Jon Dough ! 

The problem is that fgets() stores the newline in the string, so that a newline gets displayed every time you display the string. Later in this chapter, you'll learn a way to remove the newline.

Because gets() doesn't check whether the input will fit in the destination array, it is considered unsafe. Indeed, a few years ago someone exploited the fact that some UNIX operating system code used gets() to overwrite code, creating a "worm" that propagated through the UNIX network. That system code has since been replaced by gets() -free code. So in serious programming, you should use fgets() rather than gets() , but this book takes a more relaxed approach.

The scanf() Function

You've used scanf() with the %s format before to read a string. The chief difference between scanf() and gets() lies in how they decide when they have reached the end of the string: scanf() is more of a "get word" than a "get string" function. The gets() function, as you've seen, takes in all the characters up to the first newline. The scanf() function has two choices for terminating input. For either choice, the string starts at the first non-whitespace character encountered. If you use the %s format, the string runs up to (but not including) the next whitespace character (blank, tab, or newline). If you specify a field width, as in %10s , the scanf() collects up to 10 characters or up to the first whitespace character, whichever comes first (see Figure 11.3).

Figure 11.3. Field widths and scanf() .
graphics/11fig03.jpg

Recall that the scanf() function returns an integer value that equals the number of items successfully read or returns EOF if it encounters the end of file.

Listing 11.7 illustrates how scanf() works when you specify a field width.

Listing 11.7 The scan_str.c program.
 /* scan_str.c -- using scanf() */ #include <stdio.h> int main(void) {     char name1[11], name2[11];     int count;          printf("Please enter 2 names.\n");     count = scanf("%5s %10s",name1, name2);     printf("I read the %d names %s and %s.\n",            count, name1, name2);     return 0; } 

Here are three runs:

 Please enter 2 names.  Jesse Jukes  I read the 2 names Jesse and Jukes. Please enter 2 names.  Liza Applebottham  I read the 2 names Liza and Applebotth. Please enter 2 names.  Portensia Callowit  I read the 2 names Porte and nsia. 

In the first example, both names fell within the allowed size limits. In the second example, only the first 10 characters of Applebottham were read because we used a %10s format. In the third example, the last four letters of Portensia went into name2 because the second call to scanf() resumed reading input where the first ended; in this case, that was still inside the word Portensia .

You are better off using gets() to read text from the keyboard. It is easier to use, faster, and more compact. The typical use for scanf() is reading and converting a mixture of data types in some standard form. For example, if each input line contained the name of a tool, the number in stock, and the cost of the item, you might use scanf() , or you might throw together a function of your own that did some entry error-checking.

Now let's discuss how to print strings.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net