Skip to main content

Formatted Input/Output

In seeking the unattainable, simplicity only gets in the way.

Section 3.1: The printf Function

  • The printf function is designed to display the contents of a string, known as the format string. Format string may contains both ordinary characters and conversion specification (begins with the % character). Conversion specifications is a placeholder representing a value to be filled during printing. Syntax: printf(string, expr_1, expr_2, ...);

  • C compilers aren't required to check that the number of conversion specifications in a format string matches the number of output items. For instance,

    printf("%d %d\n", i);
    // Here, printf will print the value of i correctly, then print a second (meaningless) integer
    printf("%d\n", i, j);
    // Here, printf will print the value of i but doesn't show the value of j
    printf("%f %d\n", i, x); // i is an int and x is a float
    // Here, printf must obey the format string, it will dutifully display a float value, followed by an int value. Unfortunately, both will be meaningless.

Conversion Specification

  • Conversion specifications give the programmer a great deal of control over the appearance of output. It is of the form %m.pX, where m and p are integer constants and X is a letter. Both m and p are optional. If p is omitted, the period that separates m and p is also dropped.

  • The minimum field width, m, specifies the minimum number of characters to print. If the value to be printed requires fewer than m characters, the value is right-justified within the field. (In other words, extra spaces precede the value.) For instance, the specification %4d would display the number 123 as 123. Putting a minus sign in front of m causes left justification; the specification %-4d would display 123 as 123 .

  • The most common conversion specification specifiers for numbers are:

    1. d - Displays an integer in decimal (base 10) form. p indicates the minimum number of digits to display (extra zeros are added to the beginning of the number if necessary); if p is omitted, it is assumed to have a value 1. (In other words, %d is the same as %.1d)
    2. e - Displays a floating-point number in exponential format (scientific notation). p indicates how many digits should appear after the decimal point (the default is 6). If p is 0, the decimal point is not displayed.
    3. f - Displays a floating-point number in "fixed decimal" format, without an exponent. p has the same meaning as for the e specifier.
    4. g - Displays a floating-point number in either exponential format or fixed decimal format, depending on the number's size. p indicates the maximum number of significant digits (not digits after the decimal point) to be displayed. Unlike the f conversion, the g conversion won't show trailing zeros. Furthermore, if the value to be printed has no digits after the decimal point, g doesn't display the decimal point.
  • The g specifier is especially useful for displaying numbers whose size can't be predicted when the program is written or that tend to vary widely in size. g specifier uses fixed decimal format when using to print a moderately large or moderately small number. But when used to print a very large or very small number, the g specifier switches to exponential form.

Escape Sequences

  • The \n code that we use in format strings is called an escape sequence. Escape sequence enable strings to contain characters that would otherwise cause problems for the compiler, including nonprinting (control) characters and characters having a special meaning to the compiler (like "). Some of the escape sequences are:

    1. Alert bell \a
    2. Backspace \b
    3. New line \n
    4. Horizontal tab \t

Section 3.2: The scanf Function

  • scanf reads input according to a particular format. scanf will read the line, converting its characters to the numbers they represent and then assign respective values. "Tightly packed" format strings like "%d%d%f%f" are common in scanf calls. printf format strings are less likely to have adjacent conversion specifications.

  • scanf, like printf, contains several traps for the unwary.

  • Forgetting to put the & symbol in front of a variable in a call of scanf will have unpredictable-and possibly disastrous-results. A program crash is a common outcome. At the very least, the value that is read from the input won't be stored in the variable; instead, the variable will retain its old value (which may be meaningless if the variable wasn't given an initial value). The & symbol is used to create a pointer to a variable.

  • Calling scanf is a powerful but unforgiving way to read data. C programmers avoid scanf, instead reading all data in character form and converting it to numeric form later. The reason we avoid scanf is that many of our programs won't behave properly if the user enters unexpected input.

How scanf Works

  • scanf is essentially a "pattern-matching" function that tries to match up groups of input characters with conversion specifications.

  • For each conversion specification in the format string, scanf tries to locate an item of the appropriate type in the input data, skipping blank space if necessary. scanf then reads the item, stopping when it encounters a character that can't possibly belong to the item. If any item is not read successfully, scanf returns immediately without looking at the rest of the format string (or the remaining input data).

  • scanf ignores white-space characters (the space, horizontal and vertical tab, form feed, and new-line characters).

  • scanf "peeks" at the final new-line character without actually reading it. This new-line will be the first character read by the next call of scanf.

  • When asked to read an integer, scanf first searches for a digit, a plus sign or minus sign; it then reads digits until it reaches a non-digit.

  • When asked to read a floating-point number, scanf looks for:

    1. a plus or minus sign (optional), followed by
    2. a series of digits (possibly containing a decimal point), followed by
    3. an exponent (optional). An exponent consists of the letter e (or E), an optional sign, and one or more digits.
  • The %e, %f, and %g conversions are interchangeable when used with scanf; all three follow the same rules for recognizing a floating-point number.

  • Consider the following example:

    scanf("%d%d%f%f", &i, &j, &x, &y);

    The following values are given as the input:

    1-20.3-4.0e3<> // <> here represents new-line character

    Here's how scanf would process the new input:

    Conversion specification: %d. The first nonblank input character is 1; since integers can begin with 1, scanf then reads the next character, -. Recognizing that - can't appear inside an integer, scanf stores 1 into i and puts the - character back.

    Conversion specification: %d. scanf then reads the characters -2, 0, and . (period). Since an integer can't contain a decimal point, scanf stores -20 into j and puts the . character back.

    Conversion specification: %f. scanf reads the characters ., 3, and -. Since a floating-point number can't contain a minus sign after a digit, scanf stores 0.3 into x and puts the - character back.

    NOTE: Conversion specification: %f. Lastly, scanf reads the characters -, 4, ., 0, e, 3 and <> (new-line). Since a floating-point number can't contain a new-line character, scanf stores -4.0 * 10 ^ 3 into y and puts the new-line character back.

Ordinary Characters in Format Strings

  • When scanf encounters one or more consecutive white-space characters in a format string, it repeatedly reads white-space characters from the input until it reaches a non-white-space character. One white-space character in the format string will match any number of white-space characters in the input.

  • If the characters don't match, scanf puts the offending character back into the input, then aborts without further processing the format string or reading characters from the input.

  • To allow spaces after the first number (two numbers separated by a / sign), we should use the format string "%d /%d".

Confusing printf with scanf

  • Consider the follwing call of scanf:

    scanf("%d, %d", &i, &j);

    -> scanf will first look for an integer in the input, which it stores in the variable i. scanf will then try to match a comma with the next input character. If the next input character is a space, not a comma, scanf will terminate without reading a value for j.

  • Although printf format strings often end with \n, putting a new-line character at the end of a scanf format string is usually a bad idea. To scanf, a new-line character in a format string is equivalent to a space; both cause scanf to advance to the next non-white-space character. For example, if the format string is "%d\n", scanf will skip whitespace, reads an integer, then skip it to the next non-white-space character. A format string like this can cause an interactive program to "hang" until the user enters a non-blank character.

Q&A

  • %d can only match an integer written in decimal (base 10) form, while %i can match an integer expressed in octal (base 8), decimal, or hexa-decimal (base 16). If an input number has a 0 prefix (as in 056), %i treats it as an octal number; if it has a 0x or 0X (as in 0x56), %i treats it as a hex number.

  • Tab stops are eight characters apart, but C makes no guarantee.

  • Programs don't read user inputs as it is typed, instead, input is stored in a hidden buffer, to which scanf has access.

Exercises

Question 3.1

What output do the following calls of printf produce?

  1. printf("%6d,%4d", 86, 1040);
  2. printf ("%12.5e", 30.253);
  3. printf("%.4f", 83.162);
  4. printf("%-6.2g",.0000009979);

    86,1040  3.02530e+01 83.1620 1e-06


Question 3.2

Write calls of printf that display a float variable x in the following formats.

  1. Exponential notation; left-justified in a field of size 8; one digit after the decimal point.
  2. Exponential notation; right-justified in a field of size 10; six digits after the decimal point.
  3. Fixed decimal notation: left-justified in a field of size 8; three digits after the decimal point.
  4. Fixed decimal notation; right-justified in a field of size 6: no digits after the decimal point.

7.9e+02 7.891235e+02 789.123    789


Question 3.3

For each of the following pairs of scanf format strings, indicate whether or not the two strings are equivalent. If they're not. show how they can be distinguished.

  1. "%d" versus " %d"
  2. "%d-%d-%d" versus "%d -%d -%d"
  3. "%f" versus "%f "
  4. "%f,%f" versus "%f, %f"

(a) "%d" versus " %d" → No difference (b) "%d-%d-%d" versus "%d -%d -%d" → Difference (c) "%f" versus "%f " → Difference (expects a white space character) (d) "%f,%f" versus "%f, %f" → No difference

When it comes to different types of formats:

  1. "%d" expects for an integer that can be a number, a sign (plus or minus). " %d" does not really have that difference with the former expression.
  2. "%d-%d-%d" expects a number of format "<num>-<num>-<num>", so when we enter a number like "123 -567 -894", there can be problems. scanf first checks for the number "123" and sees it matches the pattern but when it encounters the whitespace character, it cannot do anything so it puts it back for the next scanf to check.
  3. "%f" expects a number, a sign (plus or minus) and decimal point or e (exponent). "%f " expects the same as the former pattern but it also expects a whitespace character so unless "\n" or "\t" or any other character is entered, it does not complete the input operation.
  4. "%f,%f" expects a floating point number, a comma and another floating point number. Similar to the pattern in part a, the latter one "%f, %f" expects the same without having any issue.

Question 3.4

Suppose that we call scanf as follows:
scanf("%d%f%d", &i, &x, &j) ;
If the user enters
10.3 5 6
what will be the values of i, x, and j after the call? (Assume that i and j are int variables and x is a float variable.)

Since the pattern scanf is looking for is int, then float and finally another int and when we enter the given values, the following may happen:
scanf encounters 10 followed by a . which is not used to describe an integer, so it is put back and 10 is stored in i. .3 is checked and since a whitespace is encountered, it stops to store .3 in x. Finally, 5 and a whitespace is encountered, so 5 is stored in j.

i = 10, x = 0.3, j = 5


Question 3.5

Suppose that we call scanf as follows:
scanf("%f%d%f", &x, &i, &y) ;
If the user enters
12.3 45.6 789
what will be the values of x, i, and y after the call? (Assume that x and y are float variables and i is an int variable.)

Similar to the process described in Exercise 4, the following values will be stored:

x = 12.3, i = 45, y = 0.6


Question 3.6

Show how to modify the addfrac.c program of Section 3.2 so that the user is allowed to enter fractions that contain spaces before and after each / character.

/* Adds two fractions */

#include <stdio.h>

int main(void)
{
int num1, denom1, num2, denom2, result_num, result_denom;

printf("Enter first fraction: ");
scanf("%d/%d", &num1, &denom1);

printf("Enter second fraction: ");
scanf("%d/%d", &num2, &denom2);

result_num = num1 * denom2 + num2 * denom1;
result_denom = denoml * denom2;
printf("The sum is %d/%d\n", result_num, result_denom);

return 0;
}
#include <stdio.h>

int main (void) {
int num1 = 0, num2 = 0;
int denom1 = 1, denom2 = 1;
int result_num = 0, result_denom = 1;

printf("Enter the first fraction: ");
scanf("%d /%d", &num1, &denom1);

Programming Projects

Project 3.1

Write a program that accepts a date from the user in the form mm/dd/yyyy and then displays it in the form yyyymmdd:
Enter a date (mm/dd/yyyy): 2/17/2011
You entered the date 20110217

#include <stdio.h>

int main (void) {
int month = 0, date = 0, year = 0;

printf("Enter a date (mm/dd/yyyy): ");
scanf("%d/%d/%d", &month, &date, &year);

printf("You entered the date %4d%2.2d%2.2d\n", year, month, date);

Project 3.2

Write a program that formats product information entered by the user. A session with the program should look like this:
Enter item number: 583
Enter unit price: 13.5
Enter purchase date (mm/dd/yyyy): 10/24/2010

Item            Unit            Purchase
Price Date
583 $ 13.50 10/24/2010

The item number and date should be left justified; the unit price should be right justified. Allow dollar amounts up to $9999.99. Hint: Use tabs to line up the columns.

#include <stdio.h>

int main (void) {
int item_number, date, month, year;
float unit_price;

printf("Enter item number: ");
scanf("%d", &item_number);
printf("Enter unit price: ");

Project 3.3

Books are identified by an International Standard Book Number (ISBN). ISBNs assigned after January I. 2007 contain 13 digits, arranged in five groups, such as 978-0-393-97950-3. (Older ISBNs use 10 digits.) The first group (the GSI prefix) is currently either 978 or 979. The group identifier specifies the language or country of origin (for example. 0 and 1 are used in English-speaking countries). The publisher code identifies the publisher (393 is the code for W. W. Norton). The item number is assigned by the publisher to identify a specific book (97950 is the code for this book). An ISBN ends with a check digit that's used to verify the accuracy of the preceding digits. Write a program that breaks down an ISBN entered by the user:
Enter ISBN: 978-0-393-97950-3
GSI prefix: 978
Group identifier: 0
Publisher code: 393
Item number: 97950
Check digit:3

Note: The number of digits in each group may vary: you can't assume that groups have the lengths shown in this example. Test your program with actual ISBN values (usually found on the back cover of a book and on the copyright page).

#include <stdio.h>

int main (void) {
int gsi_prefix, group_identifier, publisher_code, item_number, check_digits;

printf("Enter ISBN: ");
scanf("%d-%d-%d-%d-%d", &gsi_prefix, &group_identifier, &publisher_code, &item_number, &check_digits);

printf("GSI prefix: %d\n", gsi_prefix);

Project 3.4

Write a program that prompts the user to enter a telephone number in the form (xxx) xxx-xxxx and then displays the number in the form xxx.xxx.xxx:
Enter phone number [(xxx) xxx-xxxx]: (404) 817-6900
You entered 404.817.6900

#include <stdio.h>

int main (void) {
int code, first_place, second_place;

printf("Enter phone number [(xxx) xxx-xxxx]: ");
scanf("(%d) %d-%d", &code, &first_place, &second_place);

printf("You entered %d.%d.%d", code, first_place, second_place);

Project 3.5

Write a program that asks the user to enter the numbers from 1 to 16 (in any order) and then displays the numbers in a 4 by 4 arrangement, followed by the sums of the rows, columns, and diagonals:
Enter the numbers from 1 to 16 in any order: 16 3 2 13 5 10 11 8 9 6 7 12 4 15 14 1

16 3 2 13  5 10 11 8  9 6 7 12  4 15 14 1

Row sums: 34 34 34 34
Column sums: 34 34 34 34
Diagonal sums: 34 34

If the row, column, and diagonal sums are all the same (as they are in this example), the numbers are said to form a magic square. The magic square shown here appears in a 1514 engraving by artist and mathematician Albrecht Dürer. (Note that the middle numbers in the last row give the date of the engraving.)

#include <stdio.h>

int main (void) {
int num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_10;
int num_11, num_12, num_13, num_14, num_15, num_16;
// int row_sum_1, row_sum_2, row_sum_3, row_sum_4;
// int col_sum_1, col_sum_2, col_sum_3, col_sum_4;
// int diag_sum_1, diag_sum_2;

Project 3.6

Modify the addfrac.c program of Section 3.2 so that the user enters both fractions at the same time, separated by a plus sign: Enter two fractions separated by a plus sign: 5/6+3/4
The sum is 38/24

/* Adds two fractions */

#include <stdio.h>

int main(void)
{
int num1, denom1, num2, denom2, result_num, result_denom;

printf("Enter first fraction: ");
scanf("%d/%d", &num1, &denom1);

printf("Enter second fraction: ");
scanf("%d/%d", &num2, &denom2);

result_num = num1 * denom2 + num2 * denom1;
result_denom = denoml * denom2;
printf("The sum is %d/%d\n", result_num, result_denom);

return 0;
}
#include <stdio.h>

int main (void) {
int num_1, num_2;
int denom_1, denom_2;
// int result_num, result_denom;

printf("Enter two fractions separated by a plus sign: ");
scanf("%d/%d+%d/%d", &num_1, &denom_1, &num_2, &denom_2);