CHAPTER 10

Essential Input and Output Operations

In this chapter you're going to look in more detail at input from the keyboard, output to the screen, and output to a printer. The good news is that everything in this chapter is fairly easy, although there may be moments when you feel it's all becoming a bit of a memory test. Treat this as a breather from the last two chapters. After all, you don't have to memorize everything you see here; you can always come back to it when you need it.

Like most modern programming languages, the C language has no input or output capability within the language. All operations of this kind are provided by functions from standard libraries. You've been using many of these functions to provide input from the keyboard and output to the screen in all the preceding chapters.

This chapter will put all the pieces together into some semblance of order and round it out with the aspects I haven't explained so far. I'll also add a bit about printing because it's usually a fairly essential facility for a program. You don't have a program demonstrating a problem solution with this chapter for the simple reason that I don't really cover anything that requires any practice on a substantial example (it's that easy).

In this chapter you'll learn the following:

  • How to read data from the keyboard
  • How to format data for output on the screen
  • How to deal with character output
  • How to output data to a printer

Input and Output Streams

Up to now you've primarily used scanf() for keyboard input and printf() for output to the screen. Actually, there has been nothing in particular about the way you've used these functions to specify where the input came from or where the output went. The information that scanf() received could have come from anywhere, as long as it was a suitable stream of characters. Similarly, the output from printf() could have been going anywhere that could accept a stream of characters. This is no accident: the standard input/output functions in C have been designed to be device-independent, so that the transfer of data to or from a specific device isn't a concern of the programmer. The C library functions and the operating system make sure that operations with a specific device are executed correctly.

Each input source and output destination in C is called a stream. An input stream is a source of data that can be read into your program, and an output stream is a destination for data that originates in your program. A stream is independent of the physical piece of equipment involved, such as the display or the keyboard. Each device that a program uses will usually have one or more streams associated with it, depending on whether it's simply an input device such as a keyboard, or an output device such as a printer, or a device that can have both input and output operations, such as a disk drive. This is illustrated in Figure 10-1.

image

Figure 10-1.Standard and nonstandard streams

A disk drive can have multiple input and output streams because it can contain multiple files. The correspondence is between a stream and a file, not between a stream and the device. A stream can be associated with a specific file on the disk. The stream that you associate with a particular file could be an input stream, so you could only read from the file; it could be an output stream, in which case you could only write to the file; or the stream might allow input and output so reading and writing the file would both be possible. Obviously, if the stream that is associated with a file is an input stream, the file must have been written at some time so it contained some data. You could also associate a stream with a file on a CD-ROM drive. Because this device is typically read-only, the stream would, of necessity, be an input stream.

There are two further kinds of streams: character streams, which are also referred to as text streams, and binary streams. The main difference between these is that data transferred to or from character streams is treated as a sequence of characters and may be modified by the library routine concerned, according to a format specification. Data that's transferred to or from binary streams is just a sequence of bytes that isn't modified in any way. I discuss binary streams in Chapter 12 when I cover reading and writing disk files.

Standard Streams

C has three predefined standard streams that are automatically available in any program, provided, of course, that you've included the <stdio.h> header file, which contains their definitions, into your program. These standard streams are stdin, stdout, and stderr. Two other streams that are available with some systems are identified by the names stdprn and stdaux, but these are not part of the C language standard so your compiler may not support them.

No initialization or preparation is necessary to use these streams. You just have to apply the appropriate library function that sends data to them. They are each preassigned to a specific physical device, as shown in Table 10-1.

Table 10-1. Standard Streams

Stream Device
stdin Keyboard
stdout Display screen
stderr Display screen
stdprn Printer
stdaux Serial port

In this chapter I concentrate on how you can use the standard input stream, stdin, the standard output stream, stdout, and the printer stream, stdprn.

The stderr stream is simply the stream to which error messages from the C library are sent, and you can direct your own error messages to stderr if you wish. The main difference between stdout and stderr is that output to stdout is buffered in memory so the data that you write to it won't necessarily be transferred immediately to the device, whereas stderr is unbuffered so any data you write to it is always transferred immediately to the device. With a buffered stream your program transfers data to or from a buffer area in memory, and the actual data transfer to or from the physical device can occur asynchronously. This makes the input and output operations much more efficient. The advantage of using an unbuffered stream for error messages is that you can be sure that they will actually be displayed but the output operations will be inefficient; a buffered stream is efficient but may not get flushed when a program fails for some reason, so the output may never be seen. I won't discuss this further, other than to say stderr points to the display screen and can't be redirected to another device. Output to the stream stdaux is directed to the serial port and is outside the scope of this book for reasons of space rather than complexity.

Both stdin and stdout can be reassigned to files, instead of the default of keyboard and screen, by using operating system commands. This offers you a lot of flexibility. If you want to run your program several times with the same data, during testing for example, you could prepare the data as a text file and redirect stdin to the file. This enables you to rerun the program with the same data without having to re-enter it each time. By redirecting the output from your program to a file, you can easily retain it for future reference, and you could use a text editor to access it or search it.

Input from the Keyboard

There are two forms of input from the keyboard on stdin that you've already seen in previous chapters: formatted input, which is provided primarily by the scanf() function and unformatted input, in which you receive the raw character data from a function such as getchar(). There's rather more to both of these possibilities, so let's look at them in detail.

Formatted Keyboard Input

As you know, the function scanf() reads characters from the stream stdin and converts them to one or more values according to the format specifiers in a format control string. The prototype of the scanf() function is as follows:

int scanf(char *format, ... );

The format control string parameter is of type char *, a pointer to a character string as shown here. However, this usually appears as an explicit argument in the function call, such as

scanf("%lf", &variable);

But there's nothing to prevent you from writing this:

char str[] = "%lf";
scanf(str, &variable);

The scanf() function makes use of the facility of handling a variable number of arguments that you learned about in Chapter 9. The format control string is basically a coded description of how scanf() should convert the incoming character stream to the values required. Following the format control string, you can have one or more optional arguments, each of which is an address in which a corresponding converted input value is to be stored. As you've seen, this implies that each of these arguments must be a pointer or a variable name prefixed by & to define the address of the variable rather than its value.

The scanf() function reads from stdin until it comes to the end of the format control string, or until an error condition stops the input process. This sort of error is the result of input that doesn't correspond to what is expected with the current format specifier, as you'll see. Something that I haven't previously noted is that scanf() returns a value that is the count of the number of input values read. This provides a way for you to detect when an error occurs by comparing the value returned by scanf() with the number of input values you are expecting.

The wscanf() function provides exactly the same capabilities as scanf() except that the first argument to the function, which is the format control string, must be a wide character string of type wchar_t *.

Thus you could use wscanf() to read a floating-point value from the keyboard like this:

wscanf(L"%lf", &variable);

The first argument is a wide character string constant and in all other respects the function works like scanf(). If you omit the L for the wide character string literal, you will get an error message from the compiler because your argument does not match the type of the first parameter.

Of course, you could also write this:

wchar_t wstr[] = L"%lf";
wscanf(wstr, &variable);

Input Format Control Strings

The format control string that you use with scanf() or wscanf() isn't precisely the same as that used with printf(). For one thing, putting one or more whitespace characters—blank ' ', tab ' ', or newline ' '—in the format control string causes scanf() to read and ignore whitespace characters up to the next nonwhitespace character in the input. A single whitespace character in the format control string causes any number of consecutive whitespace characters to be ignored. You can therefore include as many whitespace characters as you wish in the format string to make it more readable. Note that whitespace characters are ignored by scanf() by default except when you are reading data using %c, %[], or %n specifications (see Table 10-2).

Any nonwhitespace character other than % will cause scanf() to read but not store successive occurrences of the character. If you want scanf() to ignore commas separating values in the input for instance, just precede each format specifier by a comma. There are other differences too, as you'll see when I discuss formatted output in the section "Output to the Screen" a bit later in this chapter.

The most general form of a format specifier is shown in Figure 10-2.

image

Figure 10-2. The general form of an output specifier

Let's take a look at what the various parts of this general form mean:

  • The % simply indicates the start of the format specifier. It must always be present.
  • The next * is optional. If you include it, it indicates that the next input value is to be ignored. This isn't normally used with input from the keyboard. It does become useful, however, when stdin has been reassigned to a file and you don't want to process all the values that appear within the file in your program.
  • The field width is optional. It's an integer specifying the number of characters that scanf() should assume makes up the current value being input. This allows you to input a sequence of values without spaces between them. This is also often quite useful when reading files.
  • The next character is also optional, and it can be h, L, l (the lowercase letter L), or ll (two lowercase Ls). If it's h, it can only be included with an integer conversion specifier (d, I, o, u, or x) and indicates that the input is to be converted as short. If it's l, it indicates long when preceding an int conversion specifier, and double when preceding a float conversion specifier. Prefixingthe c specification with l specifies a wide character conversion so the input is read as wchar_t. The prefix L applied to e, E, f, g, or G specifies the value is of type long double. The ll prefix applies to integer conversions and specifies that the input is to be stored as type long long.
  • The conversion character specifies the type of conversion to be carried out on the input stream and therefore must be included. The possible characters and their meanings are shown in Table 10-2.

Table 10-2. Conversion Characters and Their Meanings

Conversion Character Meaning
d Convert input to int.
i Convert input to int. If preceded by 0, then assume octal digits input. If preceded by 0x or 0X, then assume hexadecimal digits input.
o Convert input to int and assume all digits are octal.
u Convert input to unsigned int.
x Convert to int and assume all digits are hexadecimal.
c Read the next character as char (including whitespace). If you want to ignore whitespace when reading a single character, just precede the format specification by a whitespace character.
s Input a string of successive nonwhitespace characters, starting with the next nonwhitespace character.
e, f, or g Convert input to type float. A decimal point and an exponent in the input are optional.
n No input is read but the number characters that have been read from the input source up to this point are stored in the corresponding argument, which should be of type int*.

You can also read a string that consists of specific characters by placing all the possible characters between square brackets in the specification. For example, the specification %[0123456789.-] will read a numerical value as a string, so if the input is −1.25 it will be read as "-1.25". To read a string consisting of the lowercase letters a to z, you could use the specification %[abcdefghijklmnopqrstuvwxyz]. This will read any sequence of the characters that appear between the square brackets as a string, and the first character in the input that isn't in the set between the square brackets marks the end of the input. Although it isn't required by the standard, many C library implementations support the form %[a-z] to read a string consisting of any lowercase letters.

A specification using square brackets is also very useful for reading strings that are delimited by characters other than whitespace. In this case, you can specify the characters that are not in the string by using the ^ character as the first character in the set. Thus, the specification %[^,] will include everything in the string except a comma, so this form will enable you to read a series of strings separated by commas.

Table 10-3 shows a few examples of applying the various options.

Table 10-3. Examples of Options in Conversion Specifications

Specification Description
%lf Read the next value as type double
%*d Read the next integer value but don't store it
%lc Read the next character as type wchar_t
% c Read the next character as type char ignoring whitespace characters
%10lld Reads the next ten characters as an integer value of type long long
%5d Read the next five characters as an integer
%Lf Read the next value as a floating-point value of type long double
%hu Read the next value as type unsigned short

Let's exercise some of these format control strings with practical examples.

Characters in the Input Format String

You can include a sequence of one or more characters that isn't a format conversion specifier within the input format string. If you do this, you're indicating that you expect the same characters to appear in the input and that the scanf() function should read them but not store them. These have to be matched exactly, character for character, by the data in the input stream. Any variation will terminate the input scanning process in scanf().

Variations on Floating-Point Input

When you're reading floating-point values formatted using scanf(), you not only have a choice of specification that you use, but also you can enter the values in a variety of forms. You can see this with a simple example.

Reading Hexadecimal and Octal Values

As you saw earlier, you can read hexadecimal values from the input stream using the format specifier %x. For octal values you use %o. These are very straightforward, but let's see them working in an example.

Reading Characters Using scanf()

You tried reading strings in the first example, but there are more possibilities. You know that there are three format specifiers for reading one or more single-byte characters. You can read a single character and store it as type char using the format specifier %c and as type wchar_t using %lc. For a string of characters, you use either the specifier %s or the specifier %[], or if you are storing the input as wide characters, %ls or %l[], where the prefix to the conversion specification is lowercase L. In this case, the string is stored as a null-terminated string with '' as the last character. With %[] or %l[] format specification, the string to be read must include only the characters that appear between the square brackets, or if the first character between the square brackets is ^, the string must contain only characters that are not among those following the ^ characters. Thus, %[aeiou] will read a string that consists only of vowels. The first character that isn't a vowel will signal the end of the string. The specification %[^aeiou] reads a string that contains any character that isn't a vowel. The first vowel will signal the end of the string.

Note that one interesting aspect of the %[] specification is it enables you to read a string containing spaces, something that the %s specification can't do. You just need to include a space as one of the characters between the square brackets.

Pitfalls with scanf()

There are two very common mistakes people make when using scanf() that you should keep in mind:

  • Don't forget that the arguments must be pointers. Perhaps the most common error is to forget the ampersand (&) when specifying single variables as arguments to scanf(), particularly because you don't need it with printf(). Of course, the & isn't necessary if the argument is an array name or a pointer variable.
  • When reading a string, remember to ensure that there's enough space for the string to be read in, plus the terminating ''. If you don't do this, you'll overwrite something in memory, possibly even some of your program code.

String Input from the Keyboard

As you've seen, the gets() function in <stdio.h> will read a complete line of text as a string. The prototype of the function is as follows:

char *gets(char *str);

This function reads successive characters into the memory pointed to by str until you press the Enter key. It appends the terminating null, '', in place of the newline character that is read when you press the Enter key. The return value is identical to the argument, which is the address where the string has been stored. The following example provides a reminder of how it works.

Unformatted Input from the Keyboard

The getchar() function reads one character at a time from stdin. The getchar() function is defined in <stdio.h>, and its general syntax is as follows:

int getchar(void);

The getchar() function requires no arguments, and it returns the character read from the input stream. Note that this character is returned as int, and the character is displayed on the screen as it is entered from the keyboard.

With many implementations of C, the nonstandard header file <conio.h> is often included. This provides additional functions for character input and output. One of the most useful of these is getch(), which reads a character from the keyboard without displaying it on the screen. This is particularly useful when you need to prevent others from being able to see what's being keyed in—for example, when a password is being entered.

The standard header <stdio.h> also declares the ungetc() function that enables you to put a character that you have just read back into an input stream. The function requires two arguments: the first is the character to be pushed back onto the stream, and the second is the identifier for the stream, which would be stdin for the standard input stream. The ungetc() returns a value of type int that corresponds to the character pushed back onto the stream, or a special character, EOF (end-of-file), if the operation fails.

In principle you can push a succession of characters back into an input stream but only one character is guaranteed. As I noted, a failure to push a character back onto a stream will be indicated by EOF being returned by the function, so you should check for this if you are attempting to return several characters to a stream.

The ungetc() function is useful when you are reading input character by character and don't know how many characters make up a data unit. You might be reading an integer value, for example, but don't know how many digits there are. In this situation the ungetc() function makes it possible for you to read a succession of characters using getchar(), and when you find you have read a character that is not a digit, you can return it to the stream. Here's a function that ignores spaces and tabs from the standard input stream using the getchar() and ungetc() functions:

void eatspaces(void)
{
  char ch = 0;
  while(isspace(ch = getchar()));  /* Read as long as there are spaces */
  ungetc(ch, stdin);               /* Put back the nonspace character */
}

The isspace() function that is declared in the <ctype.h> header file returns true when the argument is a space character. The while loop continues to read characters as long as they are spaces or tabs, storing each character in ch. The first nonspace character that is read will end the loop, and the character will be left in ch. The call to ungetc() returns the nonblank character back to the stream for future processing.

Let's try out the getchar() and ungetc() functions in a working example.

Output to the Screen

Writing data to the command line on the screen is much easier than reading input from the keyboard. You know what data you're writing, whereas with input you have all the vagaries of possible incorrect entry of the data. The primary function for formatted output to the stdout stream is printf().

Fortunately—or unfortunately, depending how you view the chore of getting familiar with this stuff—printf() provides myriad possible variations for the output you can obtain, much more than the scope of the format specifiers associated with scanf().

Formatted Output to the Screen Using printf()

The printf() function is defined in the header file <stdio.h>, and its general form is the following:

int printf(char *format, ...);

The first parameter is the format control string. The argument for this parameter is usually passed to the function as an explicit string constant, as you've seen in all the examples, but it can be a pointer to a string that you specify elsewhere. The optional arguments to the function are the values to be output in sequence, and they must correspond in number and type with the format conversion specifiers that appear in the string that is passed as the first argument. Of course, as you've also seen in earlier examples, if the output is simply the text that appears in the control string, there are no additional arguments after the first. But where there are argument values to be output, there must be at least as many arguments as there are format specifiers. If not, the results are unpredictable. If there are more arguments than specifiers, the excess is ignored. This is because the function uses the format string as the determinant of how many arguments follow and what type they have.


Note The fact that the format string alone determines how the data is interpreted is the reason why you get the wrong result with a %d specifier combined with a long long argument.


The <stdio.h> header also declares the wprintf() function. Analogous to the situation you saw with scanf(), the wprintf() function works in exactly the same way as printf() except that it expects the first argument to be a wide character string. The format specifications are exactly the same for both functions.

The format conversion specifiers for printf() and wprintf() are a little more complicated than those you use for input with scanf() and wscanf(). The general form of an output format specifier is shown in Figure 10-3.

image

Figure 10-3. Format specifications for the printf() and wprintf() functions

You've seen most of the details before, but let's take a quick pass through the elements of this general format specifier:

  • The % sign indicates the start of the specifier, as it does for output.
  • The optional flag characters are +, -, #, and space. These affect the output, as shown in Table 10-4.

Table 10-4. Effects of the Optional Flag Characters in an Output Specification

Character Use
+ Ensures that, for signed output values, there's always a sign preceding the output value—either a plus or a minus sign. By default, only negative values have a sign.
- Specifies that the output value is left-justified in the output field and padded with blanks on the right. The default positioning of the output is right-justified.
0 Prefixes a field_width value to specify that the value should be padded with zeros to fill out the field width to the left.
# Specifies that 0 is to precede an octal output value, 0x; or 0X is to precede a hexadecimal output value; or a floating-point output value will contain a decimal point. For the g or G floating-point conversion characters, trailing zeros will also be omitted.
space Specifies that positive or zero output values are preceded by a space rather than a plus sign.
  • The optional field_width specifies the minimum number of characters for the output value. If the value requires more characters, the field is simply expanded. If it requires less than the minimum specified, it is padded with blanks, unless the field width is specified with a leading zero, as in 09, for example, where it would be filled on the left with zeros.
  • The precision specifier is also optional and is generally used with floating-point output values. A specifier of .n indicates that n decimal places are to be output. If the value to be output has more than n significant digits, it's rounded or truncated.
  • You prefix the appropriate type conversion character with the h, l (lowercase letter L), ll, or L modifier to specify that the output conversion is being applied to short, long, long long, or long double values, respectively. The l modifier applied to the c type specification specifies that the character is to be stored as type wchar_t.
  • The conversion character that you use defines how the output is to be converted for a particular type of value. Conversion characters are defined in Table 10-5.

Table 10-5. Conversion Characters in an Output Specification

Conversion Character Output Produced
Applicable to integers
d Signed decimal integer value
o Unsigned octal integer value
u Unsigned decimal integer value
x Unsigned hexadecimal integer value with lowercase hexadecimal digits a, b, c, d, e, f
X As x but with uppercase hexadecimal digits A, B, C, D, E, F
Applicable to floating-point
f Signed decimal value
e Signed decimal value with exponent
E As e but with E for exponent instead of e
g As e or f depending on size of value and precision
G As g but with E for exponent values
Applicable to characters
c Single character
s All characters until '' is reached or precision characters have been output

Believe it or not, this set of output options includes only the most important ones. If you consult the documentation accompanying your compiler, you'll find a few more.

Escape Sequences

You can include whitespace characters in the format control string for printf() and wprintf(). The characters that are referred to as whitespace are the newline, carriage return, and form-feed characters; blank (a space); and tab. Some of these are represented by escape sequences that begin with . Table 10-6 shows the most common escape sequences.

You use the escape sequence \ in format control strings when you want to output the backslash character . If this weren't the case, it would be impossible to output a backslash, because it would always be assumed that a backslash was the start of an escape sequence. To write a % character to stdout, use %%. You can't use % by itself as this would be interpreted as the start of a format specification.

Table 10-6. Common Escape Sequences

Escape Sequence Description
a Bell sound (a beep on your computer not used much these days)
 Backspace
f Form-feed or page eject
Newline
Carriage return (for printers) or move to the beginning of the current line for output to the screen
Horizontal tab

Note Of course, you can use escape sequences within any string, not just in the context of the format string for the printf() function.


Integer Output

Let's take a look at some of the variations that you haven't made much use of so far. Those with field width and precision specifiers are probably the most interesting.

Outputting Floating-Point Values

If plowing through the integer options hasn't started you nodding off, then take a quick look at the floating-point output options through working examples.

Character Output

Now that you've looked at the various possibilities for outputting numbers, let's have a look at outputting characters. There are basically four flavors of output specifications you can use with printf() and wprintf() for character data: %c and %s for single characters and strings, and %lc and %ls for single wide characters and wide character strings, respectively. You have seen how to use %s and %ls, so let's just try out single character output in an example.

Other Output Functions

In addition to the string output capabilities of printf() and wprintf(), you have the puts() function that is also declared in <stdio.h>, and which complements the gets() function. The name of the function derives from its purpose: put string. The general form of puts() is as follows:

int puts(const char *string);

The puts() function accepts a pointer to a string as an argument and writes the string to the standard output stream, stdout. The string must be terminated by ''. The parameter to puts() is const so the function does not modify the string you pass to it. The function returns a negative integer value if any errors occur on output, and a nonnegative value otherwise. The puts() function is very useful for outputting single-line messages, for example:

puts("Is there no end to input and output?");

This will output the string that is passed as the argument and then move the cursor to the next line. The function printf() requires an explicit ' ' to be included at the end of the string to do the same thing.


Note The function puts() will process embedded ' ' characters in the string you pass as the argument to generate output on multiple lines.


Unformatted Output to the Screen

Also included in <stdio.h>, and complementing the function getchar(), is the function putchar(). This has the following general form:

int putchar(int c);

The putchar() function outputs a single character, c, to stdout and returns the character that was displayed. This allows you to output a message one character at a time, which can make your programs a bit bigger, but gives you control over whether or not you output particular characters. For example, you could simply write the following to output a string:

char string[] = "Beware the Jabberwock, my son!";
puts(string);

Alternatively, you could write:

char string[] = " Beware the Jabberwock, my son!";
int i = 0;
while( string[i] != '')
  if(string[i] != ' ')
    putchar(string[i++]);

The first fragment outputs the string over two lines, like this:


Beware the Jabberwock,
my son!

The second fragment skips newline characters in the string so the output will be the following:


Beware the Jabberwock, my son!

Your use of putchar() need not be as simple as this. With putchar() you could choose to output a selected sequence of characters from the middle of a string bounded by a given delimiter, or you could selectively transform the occurrences of certain characters within a string before output, converting tabs to spaces, for example.

Formatted Output to an Array

You can write formatted data to an array of elements of type char in memory using the sprintf() function that is declared in the <stdio.h> header file. This function has the prototype:

int sprintf(char *str, const char *format, . . .);

The function writes the data specified by the third and subsequent arguments formatted according to the format string second argument. This works identically to printf() except that the data is written to the string specified by the first argument to the function. The integer returned is a count of the number of characters written to str, excluding the terminating null character.

Here's a fragment illustrating how this works:

char result[20];                /* Output from sprintf */
int count = 4;
int nchars = sprintf(result, "A dog has %d legs.", count);

The effect of this is to write the value of count to result using the format string that is the second argument. The effect is that result will contain the string "A dog has 4 legs.". The variable nchars will contain the value 17, which is the same as the return value you would get from strlen(result) after the sprintf() call has executed. The sprintf() function will return a negative integer if an encoding error occurs during the operation.

One use for the sprintf() function is to create format strings programmatically. You'll see a simple example of this in Program12.8 in Chapter 12.

Formatted Input from an Array

The sscanf() function complements the sprintf() function because it enables you to read data under the control of a format string from an array of elements of type char. The prototype looks like this:

int sscanf(const char *str, const char *format, . . .);

Data will be read from str into variables that are specified by the third and subsequent arguments according to the format string format. The function returns a count of the number of items actually read, or EOF if a failure occurs before any data values are read and stored. The end of the string is recognized as the end-of-file condition, so reaching the end of the str string before any values are converted will cause EOF to be returned.

Here's a simple illustration of how the sscanf() function works:

char *source = "Fred 94";
char name[10];
int age = 0;
int items = sscanf(source, " %s %d", name, age);

The result of executing this fragment is that name will contain the string "Fred" and age will contain the value 94. The variable items will contain the value 2 because two items are read from source.

One use for the sscanf() function is to try various ways of reading the same data. You can always read an input line into an array as a string. You can then use sscanf() to reread the same input line from the array with different format strings as many times as you like.

Sending Output to the Printer

Writing to a printer is not part of standard C, but some C libraries do support it, so it's worth mentioning. To write output to the default printer, you can use a more generalized form of the printf() function called fprintf(). This function is designed to send formatted output to any stream, and more often to files on disk, but I'll stick to printing for now. For the purpose of printing, the general form for using fprintf() is as follows:

fprintf(stdprn, format_string, argument1, argument2, ..., argumentn);

The function will return a value of type int that is a count of the number of values that were sent to stdprn. With the exception of the first argument and the extra f in the function name, fprintf() looks exactly like printf(). And so it is. If you don't have stdprn defined with your compiler and library, you'll need to consult your documentation to see how to handle printing, but many C libraries do define stdprn. You can use the same format string with the same set of specifiers to output data to your printer in exactly the same way that you display results with printf(). However, there are a couple of minor variations you need to be aware of. I'll illustrate these in the next example.

Summary

Although I chose the various specifications for formatting that you've seen in this chapter with the idea of them being as meaningful as possible, there are a lot of them. The only way you're going to become comfortable with them is through practice, and ideally this practice needs to take place in a real-world context. Understanding the various codes is one thing, but they'll probably become really familiar to you only once you've used them a few times in real programs. In the meantime, when you need a quick reminder you can always look them up in the summary Appendix D.

Exercises

The following exercises enable you to try out what you've learned in this chapter. If you get stuck, look back over the chapter for help. If you're still stuck, you can download the solutions from the Source Code/Download area of the Apress web site (http://www.apress.com), but that really should be a last resort.

Exercise 10-1. Write a program that will read, store, and output the following five types of strings on separate lines, when one of each type of string is entered on a single line without spaces between the lines:

  • Type 1: A sequence of lowercase letters followed by a digit (e.g., number1)
  • Type 2: Two words that both begin with a capital letter and have a hyphen between them (e.g., Seven-Up)
  • Type 3: A decimal value (e.g., 7.35)
  • Type 4: A sequence of upper- and lowercase letters and spaces (e.g., Oliver Hardy)
  • Type 5: A sequence of any characters except spaces and digits (e.g., floating-point)

The following is a sample of input that should be read as five separate strings:

babylon5John-Boy3.14159Stan Laurel'Winner!'

Exercise 10-2. Write a program that will read the numerical values in the following line of input, and output the total:

$3.50  ,  $4.75 , $9.95 , $2.50

Exercise 10-3. Define a function that will output an array of values of type double that is passed as an argument along with the number of elements in the array. The prototype of the function will be the following:

void show(double array[], int array_size, int field_width);

The values should be output 5 to a line, each with 2 decimal places after the decimal point and in a field width of 12. Use the function in a program that will output the values from 1.5 to 4.5 in steps of 0.3 (i.e., 1.5, 1.8, 2.1, and so on, up to 4.5).

Exercise 10-4. Define a function using the getchar() function that will read a string from stdin terminated by a character that is passed as the second argument to the function. Thus the prototype will be the following:

char *getString(char *buffer, char end_char);

The return value is the pointer that is passed as the first argument. Write a program to demonstrate the use of the function to read and output five strings that are from the keyboard, each terminated by a colon.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.151.45