Formatted I/O: printf and scanf in C Programming Language

You have been using the printf and scanf functions throughout this book. In this sec- tion, you learn about all of the options that are available for formatting data with these functions.

The first argument to both printf and scanf is a character pointer. This points to the format string. The format string specifies how the remaining arguments to the func- tion are to be displayed in the case of printf, and how the data that is read is to be interpreted in the case of scanf.

1. The printf Function

You have seen in various program examples how you could place certain characters between the % character and the specific so-called conversion character to more precisely control the formatting of the output. For example, you saw in Program 5.3A how an integer value before the conversion character could be used to specify a field width. The format characters %2i specified the display of an integer value right-justified in a field width of two columns.You also saw in exercise 6 in Chapter 5, “Program Looping,” how a minus sign could be used to left-justify a value in a field.

The general format of a printf conversion specification is as follows:

%[flags][width][.prec][hlL]type

Optional fields are enclosed in brackets and must appear in the order shown.

Tables 16.1, 16.2, and 16.3 summarize all possible characters and values that can be placed directly after the % sign and before the type specification inside a format string.

Tables 16.1 to 16.4 might appear a bit overwhelming. As you can see, many different combinations can be used to precisely control the format of your output. The best way to become familiar with the various possibilities is through experimentation. Just make certain that the number of arguments you give to the printf function matches the number of % signs in the format string (with %% as the exception, of course). And, in the case of using an * in place of an integer for the field width or precision modifiers, remember that printf is expecting an argument for each asterisk  as well.

Program 16.1 shows some of the formatting possibilities using printf.

Program 16.1   Illustrating the  printf Formats

// Program to illustrate various printf formats

#include <stdio.h>

int main (void)

{

char              c = ‘X’;

char              s[] = “abcdefghijklmnopqrstuvwxyz”;

int               i = 425;

short int         j = 17;

unsigned int      u = 0xf179U;

long int          l = 75000L;

long long int     L = 0x1234567812345678LL;

float             f = 12.978F;

double            d = -97.4583;

char             *cp = &c;

int              *ip = &i;

int             c1, c2;

 

printf (“Integers:\n”);

printf (“%i %o %x %u\n”, i, i, i, i);

printf (“%x %X %#x %#X\n”, i, i, i, i);

printf (“%+i % i %07i %.7i\n”, i, i, i, i);

printf (“%i %o %x %u\n”, j, j, j, j);

printf (“%i %o %x %u\n”, u, u, u, u);

printf (“%ld %lo %lx %lu\n”, l, l, l, l);

printf (“%lli %llo %llx %llu\n”, L, L, L, L);

 

printf (“\nFloats and Doubles:\n”);

printf (“%f %e %g\n”, f, f, f);

printf (“%.2f %.2e\n”, f, f);

printf (“%.0f %.0e\n”, f, f);

printf (“%7.2f %7.2e\n”, f, f);

printf (“%f %e %g\n”, d, d, d);

printf (“%.*f\n”, 3, d);

printf (“%*.*f\n”, 8, 2, d);

 

printf (“\nCharacters:\n”);

printf (“%c\n”, c);

printf (“%3c%3c\n”, c, c);

printf (“%x\n”, c);

 

printf (“\nStrings:\n”);

printf (“%s\n”, s);

printf (“%.5s\n”, s);

printf (“%30s\n”, s);

printf (“%20.5s\n”, s);

printf (“%-20.5s\n”, s);

 

printf (“\nPointers:\n”);

printf (“%p %p\n\n”, ip, cp);

printf (“This%n is fun.%n\n”, &c1, &c2);

printf (“c1 = %i, c2 = %i\n”, c1, c2);

 

return 0;

}

Program 16.1   Output

Integers:

425 651 1a9 425

1a9 1A9 0x1a9 0X1A9

+425 425 0000425 0000425

17 21 11 17

61817 170571 f179 61817

75000 222370 124f8 75000

1311768465173141112 110642547402215053170 1234567812345678 1311768465173141112

 

Floats and Doubles:

12.978000 1.297800e+01 12.978

12.98 1.30e+01

13 1e+01

12.98 1.30e+01

-97.458300 -9.745830e+01  -97.4583

-97.458

-97.46

Characters:

X

   X X

58

Strings: abcdefghijklmnopqrstuvwxyz abcde

abcdefghijklmnopqrstuvwxyz

abcde

abcde

Pointers:

0xbffffc20 0xbffffbf0

This is fun.

c1 = 4, c2 = 12

It’s worthwhile to take some time to explain the output in detail. The first set of output deals with the display of integers: short, long, unsigned, and “normal” ints. The first line displays i in decimal (%i), octal (%o), hexadecimal (%x), and unsigned (%u) formats. Notice that octal numbers are not preceded by a leading 0 when they are displayed.

The next line of output displays the value of i again. First, i is displayed in hexadeci-mal notation using %x. The use of a capital X (%#X) causes printf to use uppercase letters A–F instead of lowercase letters when displaying numbers in hexadecimal. The # modifi- er (%#x) causes a leading 0x to appear before the number and causes a leading 0X to appear when the capital X is used as the conversion character (%#X).

The fourth printf call first uses the + flag to force a sign to appear, even if the value is positive (normally, no sign is displayed). Then, the space modifier is used to force a leading space in front of a positive value. (Sometimes this is useful for aligning data that might be positive or negative; the positive values have a leading space; the negative ones have a minus sign.) Next, %07 is used to display the value of i right-justified within a field width of seven characters. The 0 flag specifies zero fill. Therefore, four leading zeroes are placed in front of the value of i, which is 425. The final conversion in this call, %.7i is used to display the value of i using a minimum of seven digits. The net effect is the same as specifying  %07i: Four leading zeroes are displayed, followed by the three-digit number 425.

The fifth printf call displays the value of the short int variable j in various for- mats. Any integer format can be specified to display the value of a short int.

The next printf call shows what happens when %i is used to display the value of an unsigned int. Because the value assigned to u is larger than the maximum positive value that can be stored in a signed int on the machine on which this program was run, it is displayed  as a negative number when the %i format characters are used.

The next to last printf call in this set shows how the l modifier is used to display long integers, and the final printf call in the set shows how long long integers can be displayed.

The second set of output illustrates various formatting possibilities for displaying floats and doubles. The first output line of this set shows the result of displaying a float value using %f, %e, and %g formats. As mentioned, unless specified otherwise, the %f and %e formats default to six decimal places. With the %g format, printf decides whether to display the value in either %e or %f format, depending upon the magnitude of the value and on the specified precision. If the exponent is less than –4 or greater than the optionally specified precision (remember, the default is 6), %e is used; otherwise, %f is used. In either case, trailing zeroes are automatically removed, and a decimal point is displayed only if nonzero digits follow it. In general, %g is the best format to use for displaying floating-point numbers in the most aesthetically pleasing format.

In the next line of output, the precision modifier .2 is specified to limit the display of f to two decimal places. As you can see, printf is nice enough to automatically round the value of f for you. The line that immediately follows shows the use of the .0 preci- sion modifier to suppress the display of any decimal places, including the decimal point, in the %f format. Once again, the value of f is automatically rounded.

The modifiers 7.2, as used for generating the next line of output, specify that the value is to be displayed in a minimum of seven columns, to two decimal places of accu- racy. Because both values need fewer than seven columns to be displayed, printf right- justifies the value (adding spaces on the left) within the specified field width.

In the next three lines of output, the value of the double variable d is displayed with various formats. The same format characters are used for the display of floats and double values, because, as you’ll once again recall, floats are automatically converted to doubles when passed as arguments to functions. The printf call

printf (“%.*f\n”, 3, d);

specifies that the value of d is to be displayed to three decimal places. The asterisk after the period in the format specification instructs printf to take the next argument to the function as the value of the precision. In this case, the next argument is 3. This value could also have been specified by a variable, as in

printf (“%.*f\n”, accuracy, d);

which makes this feature useful for dynamically changing the format of a display.

The final line of the floats and doubles set shows the result of using the format characters %*.*f for displaying the value of d. In this case, both the field width and the precision are given as arguments to the function, as indicated by the two asterisks in the format string. Because the first argument after the format string is 8, this is taken as the field width. The next argument, 2, is taken as the precision. The value of d is, therefore, displayed to two decimal places in a field size of eight characters. Notice that the minus sign as well as the decimal point are included in the field-width count. This is true for any field specifier.

In the next set of program output, the character c, which was initially set to the char- acter X, is displayed in various formats. The first time it is displayed using the familiar %c format characters. On the next line, it is displayed twice with a field-width specification of 3. This results in the display of the character with two leading spaces.

A character can be displayed using any integer format specification. In the next line of output, the value of c is displayed in hexadecimal. The output indicates that on this machine the character X is internally represented by the number hexadecimal 58.

In the final set of program output, the character string s is displayed. The first time it is displayed with the normal %s format characters. Then, a precision specification of 5 is used to display just the first five characters from the string. This results in the display of the first five letters of the alphabet.

In the third output line from this set, the entire character string is once again dis- played, this time using a field-width specification of 30. As you can see, the string is dis- played right-justified in the field.

The final two lines from this set show five characters from the string s being dis- played in a field-width size of 20. The first time, these five characters are displayed right- justified in the field. The second time, the minus sign results in the display of the first five letters left-justified in the field. The vertical bar character was printed to verify that the format characters %-20.5s actually result in the display of 20 characters at the terminal (five letters followed by 15 spaces).

The %p characters are used to display the value of a pointer. Here, you are displaying the integer pointer ip and the character pointer cp.You should note that you will proba- bly get different values displayed on your system because your pointers will most likely contain different addresses.

The format of the output when using %p is implementation-defined, but in this example, the pointers are displayed in hexadecimal format. According to the output, the pointer variable ip contained the address bffffc20 hexadecimal, and the pointer cp con- tained the address bffffbf0.

The final set of output shows the use of the %n format characters. In this case, the corresponding argument to printf must be of type pointer to int, unless a type modifi- er of hh, h, l, ll, j, z, or t is specified. printf actually stores the number of characters it has written so far into the integer pointed to by this argument. So, the first occurrence of %n causes printf to store the value 4 inside the integer variable c1 because that’s how many characters have been written so far by this call. The second occurrence of %n caus- es the value 12 to be stored inside c2. This is because 12 characters had been displayed at that point by printf. Notice that inclusion of the %n inside the format string has no effect on the actual output produced by printf.

2. The scanf Function

Like the printf function, many more formatting options can be specified inside the for- mat string of a scanf call than have been illustrated up to this point. As with printf, scanf takes optional modifiers between the % and the conversion character. These optional modifiers are summarized in Table 16.5. The possible conversion characters that can be specified are summarized in Table 16.6.

When the scanf function searches the input stream for a value to be read, it always bypasses any leading so-called whitespace characters, where whitespace refers  to either a blank space, horizontal tab (‘\t’), vertical tab (‘\v’), carriage return (‘\r’), newline (‘\n’), or form-feed character (‘\f’). The exceptions are in the case of the %c format characters—in which case, the next character from the input, no matter what it is, is read—and in the case of the bracketed character string—in which case, the characters contained in the brackets (or not contained in the brackets) specify the permissible char- acters of the string.

When scanf reads in a particular value, reading of the value terminates as soon as the number of characters specified by the field width is reached (if supplied) or until a char- acter that is not valid for the value being read is encountered. In the case of integers, valid characters are an optionally signed sequence of digits that are valid for the base of the integer that is being read (decimal: 0–9, octal: 0–7, hexadecimal: 0–9, a–f, or A–F). For floats, permissible characters are an optionally signed sequence of decimal digits, followed by an optional decimal point and another sequence of decimal digits, all of which can be followed by the letter e (or E) and an optionally signed exponent. In the case of %a,a hexadecimal floating value can be supplied in the format of a leading 0x, followed by a sequence of hexadecimal digits with an optional decimal point, followed by an optional exponent preceded by the letter p (or P).

For character strings read with the %s format, any nonwhitespace character is valid. In the case of %c format, all characters are valid. Finally, in the case of the bracketed string read, valid characters are only those enclosed within the brackets (or not enclosed within the brackets if the ^ character is used after the open bracket).

Recall from Chapter 9, “Working with Structures,” when you wrote the programs that prompted the user to enter the time from the terminal, any nonformat characters that were specified in the format string of the scanf call were expected on the input. So, for example, the scanf call

scanf (“%i:%i:%i”, &hour, &minutes, &seconds);

means that three integer values are to be read in and stored in the variables hour, minutes, and seconds, respectively. Inside the format string, the : character specifies that colons are expected as separators between the three integer values.

To specify that a percent sign is expected as input, double percent signs are included in the format string, as follows:

scanf (“%i%%”, &percentage);

Whitespace characters inside a format string match an arbitrary number of whitespace characters on the input. So, the call

scanf (“%i%c”, &i, &c);

with the line of text

29   w

assigns the value 29 to i and a space character to c because this is the character that appears immediately after the characters 29 on the input. If the following scanf call is made instead:

scanf (“%i %c”, &i, &c);

and the same line of text is entered, the value 29 is assigned to i and the character ‘w’ to c because the blank space in the format string causes the scanf function to ignore any leading whitespace characters after the characters 29 have been read.

Table 16.5 indicates that an asterisk can be used to skip fields. If the scanf call

scanf (“%i %5c %*f %s”, &i1, text, string);

is executed and the following line of text is typed in:

144abcde  736.55    (wine and cheese)

the value 144 is stored in i1; the five characters abcde are stored in the character array text; the floating value 736.55 is matched but not assigned; and the character string “(wine” is stored in string, terminated by a null. The next call to scanf picks up where the last one left off. So, a subsequent call such as

scanf (“%s %s %i”, string2, string3, &i2);

has the effect of storing the character string “and” in string2 and the string “cheese)”

in string3, and causes the function to wait for an integer value to be typed.

Remember that scanf expects pointers to the variables where the values that are read in are to be stored.You know from Chapter 11, “Pointers,” why this is necessary—so that scanf can make changes to the variables; that is, store the values that it reads into them. Remember also that to specify a pointer to an array, only the name of the array needs be specified. So, if text is defined as an appropriately  sized array of characters, the scanf call

scanf (“%80c”, text);

reads the next 80 characters from the input and stores them in text.

The scanf call

scanf (“%[^/]”, text);

indicates that the string to be read can consist of any character except for a slash. Using the preceding call on the following line of text

(wine and cheese)/

has the effect of storing the string “(wine and cheese)” in text because the string is not terminated until the / is matched (which is also the character read by scanf on the next call).

To read an entire line from the terminal into the character array buf, you can specify that the newline character at the end of the line is your string terminator:

scanf (“%[^\n]\n”, buf);

The newline character is repeated outside the brackets so that scanf matches it and does not read it the next time it’s called. (Remember, scanf always continues reading from the character that terminated its last call.)

When a value is read that does not match a value expected by scanf (for example, typing in the character x when an integer is expected), scanf does not read any further items from the input and immediately returns. Because the function returns the number of items that were successfully read and assigned to variables in your program, this value can be tested to determine if any errors occurred on the input. For example, the call

if ( scanf (“%i %f %i”, &i, &f, &l) != 3 )

printf (“Error on input\n”);

tests to make certain that scanf successfully read and assigned three values. If not, an appropriate message is displayed.

Remember, the return value from scanf indicates the number of values read and assigned, so the call

scanf (“%i %*d %i”, &i1, &i3)

returns 2 when successful and not 3 because you are reading and assigning two integers (skipping one in between). Note also that the use of %n (to obtain the number of charac- ters read so far) does not get included in the value returned by scanf.

Experiment with the various formatting options provided by the scanf function. As with the printf function, a good understanding of these various formats can be obtained only by trying them in actual program examples.

Source: Kochan Stephen G. (2004), Programming in C: A Complete Introduction to the C Programming Language, Sams; Subsequent edition.

Leave a Reply

Your email address will not be published. Required fields are marked *