Sei sulla pagina 1di 15

Chapter: 10

Input and Output in C

Objectives:

In this lesson, you will learn to:


• Implement standard I/O library and its various types
• Make proper use of output function printf and input function scanf
• Implement file accessing functions and manipulate variable arguments
• Apply proper I/O functions for string, char etc.
• Implement error handling and exit functions.

Chapter Name Page 1 of


15
Standard Input and Output in C
In the world of ‘real programming', it has often been observed that the I/O support provided by
several programming languages has not yet touched the required quality, which gives a
feeling that the language designers have lost their interest in this area. This reason has greatly
influenced the wide used of such languages. In C we don't find such problems as the C
language makes use of library functions to conduct all the I/O operations. Since, instead of
being forced to change the language itself, the library functions ensure that the system
designers can provide tailored I/O.
A library package known as the ‘Standard I/O Library’ or stdio evolved with C has established
itself as the most flexible and portable package and therefore is a part of the Standard.
The stdio package has been developed to ensure that C programs can be written to run on
both the unstructured binary files and files containing readable text. Since many operating
systems do maintain a distinct structure between these two types of file models.
The I/O processing
The I/O model does not differentiate between various types of physical devices supporting the
I/O. Each source or link to data file is taken as a stream of bytes and is processed in the same
manner. A character is the smallest entity that can be represented in C and hence access to a
file is authorized at any character boundary. A movable point, known as the file position
indicator supports access to any number of characters. The characters would be accessed in
a sequence order from this point, and the position indicator shifts accordingly. Initially the
position indicator is set to the starting point of a file when it is opened, but can be set as per
the positioning requests. An implementation related effect is observed on the stream's file
position indicator whenever a file is opened through append mode.
Note
The file position indicator is ignored, where there is no possibility of random access.

The overall effect is to provide sequential reads or writes unless the stream was opened in
append mode, or the file position indicator is explicitly moved.
There are two types of file, text files and binary files, which when once opened for I/O within a
program, are processed as text streams and binary streams. The stdio package restricts the
processing of the contents in a file directly as it process data in form of streams.
The stdio package process three kinds of data streams:
• stdin - standard input
• stdout - standard output
• stderr - standard error
For instance -
FILE *stdin;
FILE *stdout;
FILE *stderr;
#include <stdio.h>
By default, standard input (stdin) is for the input supplied by user from the keyboard, while
standard output and standard error are for regular and error output respectively to be printed
on the screen. These pointers can be passed as arguments to functions like getchar and
putchar that make dynamic use of stdin and stdout. These pointers are predefined, and

Chapter Name Page 2 of


15
constant. These streams can be redirected to disk files or to other devices by using the
freopen function. The operating system permits a program's standard input and output to be
redirected at the command level.
Note
These streams are automatically accessed by the runtime environment, since they
should not be accessed explicitly.

Thinking Questions

1. What do you mean by file position indicator?


2. What happens when a file is opened through append mode?
3. Name the three types of data streams processed by stdio package?

Formatted Output -printf

Formatted I/O uses a number of associated functions to resolve the format of the I/O from a
format string. In case of output, the format string includes plain text, which is copied to the
output stream, and embedded format specifications which require some special processing
and conversion of the arguments supplied.
Each format specification starts with a % character, and is followed by the rest of the
specification.
The output function printf converts, formats, and prints its arguments on the output screen as
per the controlled specifications.
Syntax:
int printf(char *format, arg1, arg2, ...);
The format string includes two types of elements: ordinary characters, which are copied to the
output stream, and conversion specifications, each of which initiates processing and printing
of the next consecutive argument to printf. Each conversion specification should begin with a
‘%’ and terminate with a conversion character. Between the % and the conversion character
there may be, some required fields which include:
%<flags><field width><precision><length>conversion char

The format specification takes the form provided above for the functions performing
conversions, along with the optional parts that are enclosed in brackets for which the
explanation is provided below:

Flags:
• (-) justifies the adjustment of the converted argument towards left
• (+) justifies the adjustment of the converted argument towards right (by default)
• A signed conversion must start with a plus or minus sign
• Space replaces the first character of the signed conversion with a blank-space, if it is not
a sign. Overridden by (+) if present. .

Chapter Name Page 3 of


15
(#) performs the following tasks:
• Facilitates an optional form of output.
• Includes 0 as the first digit in an octal conversion
• inserts 0X in front of a non-zero hexadecimal conversion
• forces a decimal point in all floating point conversions even if one is not necessary

Field-width
• A decimal integer is used to supply the field width required to hold the output that can be
changed as per the need.
• When supplied with an asterisk, the consequent argument is converted into an integer
and is then taken as a value for the field width;
• When the value is negative it is taken as a (–) flag followed by a positive field width value.
• When the output would acquire less space than the specified value, the field width is
padded with spaces if the supplied value starts with spaces and zeros if it starts with a
zero, to fit the allocated size.
• The padding is performed on the left unless provided with the left-adjustment flag.

Precision
It starts with a period (.). It indicates the number of characters to be printed from a string for s
conversion.

Length
Here ‘h’ indicates that the integer is required to be printed as short. Whereas ‘l’ indicates that
the integer is required to be printed as long and is only applied to the floating-point integers.

Conversion
The table below shows the effect of conversion on several specifiers:

Default
Specifier Effect
precision

d signed decimal 1

i signed decimal 1

u unsigned decimal 1

o unsigned octal 1

x unsigned hexadecimal (0–f) 1

X unsigned hexadecimal (0–F) 1

Precision indicate least number of digits,


expanded with leading zeros if required. While,

Chapter Name Page 4 of


15
Default
Specifier Effect
precision

printing a zero value with zero precision, outputs


no characters.

Print a double with rounded precision digits after


f the decimal point. To conceal the decimal point 6
a precision of explicitly zero should be used.

Print a double in rounded exponential format,


with one digit before the decimal point following
with precision. A precision of zero conceals the
e, E 6
decimal point. There will be minimum two digits
in the exponent, which is printed as 6.66e23 in e
format, or 6.66E23 in E format.

Use style f, or e (E with G) depending on the


exponent. If exponent > −4 or ≥ precision, f is
g,G not used. Trailing zeros are concealed. A unspecified
decimal point is printed only if there is a
subsequent digit.

Converts the int argument into an unsigned char


c
and outputs the resultant character.

Print a string long up to precision digits. If


precision is not indicated, or is greater than the
s infinite
length of the string, the string must be
terminated as NUL.

Prints the value of a (void *) pointer in a system-


p
dependent manner.

The argument should be a pointer to an integer.


n The number of characters printed to this point by
this call will be written in integer.

% A% —

Table: Conversions

The functions that use these formats are described in the next Table.
All the above conversions require <stdio.h> as shown below:
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
int printf(const char *format, ...);
int sprintf(char *s, const char *format, ...);

Chapter Name Page 5 of


15
Name Purpose

General formatted output as specified. Output is printed to the file pointed to


fprintf
by stream.

printf Similar to fprintf with a first argument equal to stdout.

Similar to fprintf except that the output is not printed to a file, but printed into
sprintf
the array of characters pointed to by s.

Table: Functions performing conversion output

All the above functions pass number of characters as output or a (-) value on error. The
trailing null is ignored by sprintf. Implementations should allow a minimum of 509 characters to
be returned by any single conversion.

Variable length Argument Lists

All of the functions we have discussed until now were able to accept only a fixed number of
arguments, and we have always been cautious while calling functions by supplying them the
exact number of arguments. We have also been using explicit function prototype declarations
to let the compiler verify that the functions we call include exact number of arguments. But we
don’t apply such hard and fast rules in case of printf, as sometimes we call it with single
argument and sometimes we pass several arguments to it.
Now the question is, why is this ignored by the compiler? How printf is able to access the
additional arguments passed to it, without being aware of the quantity and types of those
arguments?
Here we'll discuss the variable-length argument list, which is often employed under the
shorthand term "varargs" and which permits functions such as printf to be executed.
Declaring "varargs" Functions
According to the ANSI/ISO C Standard whenever a varargs function is called, all the functions
that takes a variable number of arguments should be defined explicitly, and also the function
prototype should be available. The reason for the prototypes to be strictly included is that the
varargs functions may use special calling sequences for which the compiler might have to
generate some special code.
A variable-length argument list is identified by an ellipsis in the prototype. For instance, the
prototype for printf in <stdio.h>, looks like:
extern int printf(const char *, ...);
The three dots ... printed above are the ellipsis notation. This syntax indicates the presence of
a variable-length argument list. This prototype indicates that the first argument accepted by
printf's is of type const char *, and that it takes a variable number of additional arguments.
Declaring a "varargs" Function
Syntax: showing the outline of the function

Chapter Name Page 6 of


15
void myprintf(const char *fmt, ...)
{
}

Let’s understand it through an example:

#include <stdio.h>
#include<conio.h>

void myprintf(const char *fmt, ...)


{
const char *p;

for(p = fmt; *p != '\0'; p++)


{
if(*p != '%')
putchar(*p);
else {
printf(" handle it specially");
}
}
}
void main()
{
char a,b;
myprintf(&a,&b);
getch();
}

In the example above, the myprintf function takes the first argument of type const char *. In the
statement for(p = fmt; *p != '\0'; p++), a loop is created where each argument is taken one by
one until the pointer reaches null.

Formatted Input- Scanf

A number of functions exist for the purpose of accepting input from the user, and are
equivalent to the printf community. The most proper difference between the two function
groups is that the scanf group requires pointers to be passed to their arguments, so that the
values can be accepted and allocated to the appropriate targets. A very common error occurs
while implementing such function, which even the compiler cannot detect, is when we forget to
pass a pointer. This error is prevented by the variable argument list.
Interpretation of a stream of input data is controlled by using the format string, which consists
of values to be allocated to the objects pointed to by the remaining arguments to scanf. The
format string may include:
White space:
This enables the input stream to be processed up to the next non-white-space character.
Ordinary character:
It can be anything excluding white-space or % characters. The next character in the input
stream must come under this group of character.

Chapter Name Page 7 of


15
Conversion specification:
This includes a % character, followed by an optional * character, followed by an optional
nonzero decimal integer indicating the maximum field width, an optional h, l or L to specify the
length of the conversion and finally a relative conversion specifier.
Note
The use of h, l, or L will have an effect on the type of pointer to be used.

An input field is a series of non-spaced characters starting right from the first non-spaced
character in the input, excluding the conversion specifiers c, n and [. It ends at the first odd
character or when the input field width is achieved.
The result is supplied to the place pointed by the corresponding argument, unless the
conversion is concealed using the *, as discussed before. The conversion specifiers which
could be used include:
dioux
Convert a signed integer, a signed integer in a form supported by strtol, an octal integer, an
unsigned integer and a hexadecimal integer respectively.
efg
Convert a float (not a double).
s
Takes string as input, and attaches null at the end. The string gets terminated when a white-
space is received as input that is not taken as a part of the string.
c
Reads a single character; white space is important here. To read the first non-white spaced
character, use %1s. A field width specifies that an array of characters is to be processed.
p
Reads a (void *) pointer printed out earlier using the %p of one of the printfs.
%
A % is necessary in the input, no allocation is made.
n
Returns the number of characters read by this call to this point in form of integers.
The Table below shows the size of specifiers:

Specifier Modifies Converts


l dioux long int

h dioux short int

l ef double

L ef long double

Table. Size specifiers

Chapter Name Page 8 of


15
Declaration syntax
#include <stdio.h>
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *s, const char *format, ...);
int scanf(const char *format, ...);
Fscanf reads input from the pointed stream, scanf is similar to fscanf with a first argument of
stdin, and sscanf reads input from the pointed array of characters.
EOF (end of file) is returned, if there occurs an error in input prior to any conversion. Or else,
the numbers of conversions performed successfully are supplied, which might be null if no
conversions are achieved.
A failure in input occurs by reaching EOF or the end of the input string. A failure in conversion
occurs due to failure in matching the appropriate pattern for a specific conversion.

File Access

Programming Random Access File I/O in C


Apart from the effortless applications, most of the programs require reading or writing on files.
It might require reading a config file, or a text parser or something more complex. The basic
operations associated with a file are:
• fopen - open a file- required to specify how it should be opened (read/write) and its type
(binary/text)
• fclose - closes an opened file
• fread - reads from a file
• fwrite - writes to a file
• fseek/fsetpos - points a file pointer to the required place in a file.
• ftell/fgetpos - tells you the location of the file pointer.
There are two main types of files: text file and binary file from which binary files are known to
be the simplest one to deal with. The first four operations provided above are both for text and
random access files. The last two operations are just for random access.
Random access provides user the facility to move to any portion of a file and read or write
data from it without caring about the remaining portion of the file. Since years ago, data was
supposed to be stored on large reels of computer tape, so the only way to move to a required
point on the tape was to read the entire content and search the position of the data stored on
the tape. Then disks being used now a days allows the user to read any part of a file directly.
Programming with Binary Files
A file of some length that records bytes with values between the range 0 to 0xff (0 to 255) is
called a binary file. These bytes posses no special meaning unlike in a text file where the
value 13 represents carriage return, 10 represents line feed, 26 represents end of file and
which requires software reading text files to deal with.
In modern terms binary files are called as a stream of bytes and also some of the modern
languages support processing with streams rather than files. The important thing to consider is
the stream of data rather than the location from where it has been called. C allows the
processing of data either in terms of files or streams. Imagine a file or stream of data in the

Chapter Name Page 9 of


15
form of a very long array. It is the random access which can help you to read or write to any
part of this array or else with sequential you would have to loop through it from the beginning
like the big tape.
Example:
The example below shows a binary file being accessed for writing, with a text string (char *)
being written to it. Generally you would use a text file for this purpose but this example will
show you how to write text in a binary file.
#include <stdio.h>
#include <string.h>
int main(int argc, char * argv[]);
{
const char * filename="test.txt";
const char * mytext="There is only one and that is me.";
int byteswritten=0;
FILE * ft= fopen(filename, "wb");
if (ft)
{
fwrite(mytext,sizeof(char),strlen(mytext), ft);
fclose( ft );
}
printf("len of mytext = %i ",strlen(mytext));
return 0;
}

This program opens a binary file and then writes a char * (string) into it. The FILE * variable is
returned from the fopen() call. If this fails it returns 0.
The fopen() command tries to access the mentioned file. Here it is test.txt in the same folder
as the application. If you can remember, when the file includes a path then all the back spaces
should be doubled up. i.e., "c:\file\test.txt" is not correct; it should be "c:\\file\\test.txt".
The filemode "wb" indicates that we are writing to a binary file. If the file doesn't exist it will be
created, and if it does then whatever it includes would be automatically deleted. If the call to
fopen fails due to any reason, the fopen returns the value 0.
fwrite(mytext,sizeof(char),strlen(mytext), ft);

The fwrite() function returns the specified text. The second and third parameters represent the
size of the characters and the length of the string, respectively. Both are declared as size_t
which is unsigned integer. The resultant of this call write count items of the specified size.
Note
With binary files, even if you are writing a string (char *) it does not adds any carriage
return or line feed characters. If it is required, then it must be explicitly included in the
string.

Error Handling - Stderr and Exit

The processing of errors in cat is not perfect. The problem is that if any of the files encounters
some problem while accessing, an analytical report is received at the end of the concatenated

Chapter Name Page 10 of


15
output. That might also be appropriate if the resultant is printed to a screen, but if it is has
been supplied to a file or to any other program via a pipeline, it is really a problem.
To handle this situation in a proper manner, a second output stream, called stderr, is allocated
to a program in the way similar to that of stdin and stdout. Output printed on stderr usually
appears on the screen even if the standard output is retransmitted.
Let’s see it through an example:
#include <stdio.h>

/* cat: concatenate files */


main(int argc, char *argv[])
{
FILE *sp;
void filecopy(FILE *, FILE *);
char *pname = argv[0]; /* program name for errors */

if (argc == 1 ) /* no args; copy standard input */


filecopy(stdin, stdout);
else
while (--argc > 0)
if ((sp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: can't open %s\n",
pname, *argv);
exit(1);
} else {
filecopy(sp, stdout);
fclose(sp);
}
if (ferror(stdout))
{
fprintf(stderr, "%s: error writing stdout\n", program);
exit(2);
}
exit(0);
}
The program uses two ways to indicate errors. First, the analytical report output produced by
fprintf moves to stderr, there it identifies its way to the screen rather than moving out through a
pipeline or into an output file. Here in the declaration above we included the program name
from argv[0], so the source of error is recognized once this program is used with others.
Second, method involves the use of standard library function exit, which when called
terminates the execution of the program. The argument exit is available to be called by any
other process in need. So the accurateness of the process can be easily determined
whenever it is called by some other program as a sub-process. The value returned after
execution signals the status of the program, wherein the return value of 0 sates that there is
no problem, whereas a nonzero value states that something is wrong with the process or
within the program. For each open output file exit calls fclose function to flush out any
temporarily saved output.
Within main, return expr is similar to exit(expr). An advantage associated with exit is that it can
be called from any other functions; also the calls to it can be easily searched using a pattern-
searching program.
If there occurs any error on the stream sp, the function ferror returns a non-zero value.
int ferror(FILE *sp)

Chapter Name Page 11 of


15
A production program should check for output errors as although they rarely occur, but often
they do. For instance, when a disk crosses its storage limits.
The function feof(FILE *) is equivalent to ferror; it returns non-zero whenever the specified file
encounters end of file.
int feof(FILE *fp)
The exit status should be taken seriously fro any important program to get desired sensible
and usable values.

Line Input and Output

The standard library supports an input and output routine fgets, which is eqivalent to the
getline function:

char *fgets(char *line, int maxline, FILE *fp)

Here, fgets takes the next line including a new line as input from file fp into the character array
line. It would continue reading the characters upto maxline-1. The resultant line ends with '\0'.
Generally fgets outputs line but when the file ends or produces error, it returns NULL.
For output, the function fputs prints a string to a file that does not need to include a newline
For instance:
int fputs(char *line, FILE *fp)

Here if an error occurs, it returns EOF (end of file) or otherwise non-negative.


The library functions gets and puts are very much equivalent to fgets and fputs, but are
operated using stdin and stdout. By observing the behavior of gets and puts, it can be noticed
that gets deletes the terminating '\n' whereas puts adds it.
Here we have demonstrated that fgets and fputs perform the same task as achieved by gets
and puts:
/* fgets: get at most n chars from ccp */

#include<stdio.h>
#include<conio.h>
void main()
{
char *fgets(char *h, int n, FILE *ccp)
{
register int i;
register char *rs;

rs = h;
while (--n > 0 && (i = getc(ccp)) != EOF)
if ((*rs++ = i) == '\n')
break;
*rs = '\0';
return (i == EOF && rs == h) ? NULL: h;
}

Chapter Name Page 12 of


15
/* fputs: put string h on file ccp */
int fputs(char *h, FILE *ccp)
{
int i;

while (i = *h++)
putc(i, ccp);
return ferror(ccp) ? EOF: 0;
}
}

The standard library produces different outputs for ferror and fputs
for no exact reason.

Let’s take another example, here getline is implemented using fgets:

/* getline: read a line, return length */


int getline(char *line, int max)
{
if (fgets(line, max, stdin) == NULL)
return 0;
else
return strlen(line);
}

The example given above shows the use of fgets in receiving input from users and fputs in
printing output on the screen. The fgets function takes input until EOF (end of file is
encountered). In explanation for the later program a function getline is implemented using
fgets.
If input is equal to null it returns 0, else returns the next line.

Thinking Questions

4. Questions Numbering (Arial, 12, Bold)

Answer Heading / Result: (Arial, 11)

Solved Examples
Ex 1: Reading in a character entered by the user.

#include <stdio.h> \* Reading input by calling getc() */

main()
{
int ch;
printf("Please type in one character:\n");

Chapter Name Page 13 of


15
ch = getc( stdin );
printf("The character you just entered is: %c\n", ch);
return 0;
}

Explanation:
In line 4, an integer variable, ch, is defined; later in line 6 it is allocated the return value from the
getc() function.
Line 5 requests the user to enter one character.
The printf() function in line 7 uses the default standard output stdout to print output on the
screen.
In line 8, the standard input stdin is passed to the getc() function. When user inputs a character,
the getc() function returns the integer value of the character.

Ex 2: Specifying minimum field width

#include <stdio.h>
main()
{
int n1, n2;
n1 = 66;
n2 = 66666;
printf("%d\n", n1);
printf("%d\n", n2);
printf("%5d\n", n1);
printf("%05d\n", n1);
printf("%2d\n", n2);
return 0;
}

Explanation:
In the program above, two integer variables n1 and n2 are declared and assigned 6 and 66666
respectively.
Without using any minimum field width specifiers, the printf () function prints out the two
integers.
The statement takes two character spaces in the statement printf("%2d\n", n2), but you still see
the full-size output of 66666 that means that when the minimum field width is shorter than the
width of the output, the latter is taken.
while the output of the statement printf("%05d\n", n1) takes five character spaces, with three
blank spaces plus two character spaces of 66
In line 12, a minimum field width, 5, is specified by %5d. The output from line 12 therefore
takes five character spaces.

Practical(s)

Chapter Name Page 14 of


15
Summary
• C language uses library functions to manage all the I/O operations. A library package
known as the ‘Standard I/O Library’ or stdio implemented with C has established itself as
the most flexible and portable package and therefore is a part of the Standard. The stdio
package has been advanced to ensure that C programs can be written to run on both the
unstructured binary files and files containing readable text.
• Each format specification commences with a % character, and is followed by the rest of
the specification.
• The variable-length argument list, which is often employed under the shorthand term
"varargs" permits functions such as printf to be executed.
• Interpretation of a stream of input data is managed by using the format string, which
comprise of values to be allocated to the objects pointed to by the remaining arguments to
scanf.
• An input field is a stream of non-spaced characters right from the first non-spaced
character in the input, excluding the conversion specifiers c, n and [.
• A failure in input occurs by reaching EOF or the end of the input string. A failure in
conversion occurs as a result of failure in matching the appropriate pattern for a specific
conversion.
• Random access provides user the facility access any portion of a file and read or write
data from it without caring about the remaining portion of the file.
• A production program should always check for output errors as although they rarely
occur, but often they do. For instance, when a disk reaches its storage limits.

Chapter Name Page 15 of


15

Potrebbero piacerti anche