Formatted and Unformatted Input/Output Functions in C

Question: What is Formatted and Unformatted Line I/O in C Programming?

Answer: Unformatted line I/O merely reads strings and/or writes strings. There’s no conversions of numeric values according to specific format. gets and puts family of functions perform unformatted line I/O. Let’s see their prototypes below

    char *fgets(char *buffer, int buffer_size, FILE *stream);
    char *gets(char *buffer);
    int fputs(char const *buffer, FILE *stream);
    int puts(char const *buffer);

Notice that fgets() reads from a given file, including stdin, into a buffer of buffer_size argument. fgets() never overflows its buffer because it reads buffer_size – 1 characters, terminates input with NULL character making whatever stored in the buffer a string. Therefore, fgets() can’t read into a buffer of size less than 2 bytes. fputs() is used to write to a given file, including stdout. Notice that size argument isn’t included in the function prototype because it writes string stored in the buffer. Remember that fgets() must be used with fputs() function.

gets() is used to read from stdin. Notice that it doesn’t contain size argument therefore if gets() reads a larger string into a short buffer it overflows the buffer, writing into whatever follows buffer, overwriting the values thereon. The only way to guard against overflow is to declare a large sized buffer. However there’s always a possibility of overflowing of buffer. Remember that gets() doesn’t terminate it’s input with a terminating newline character. puts() writes to stdout. It adds a newline character to each output. Therefore, gets() and puts() must be used together for reading and writing. Let’s see a simple C program performing unformatted line I/O with files (for input and for output) passed as command-line arguments,

/*fgets.c -- fgets() returns pointer to buffer or NULL */
#include <stdio.h>
#include <stdlib.h>
 
#define BUFSIZE 101
 
int main(int argc, char *argv[])
{
    FILE *input;
    FILE *output;
    char buffer[BUFSIZE];
 
   if (argc != 3) {
        perror("File arguments on command-line");
        exit(EXIT_FAILURE);
   }
 
    input = fopen(argv[1], "r");
    if (input == NULL) {
        perror("File open");
        exit(EXIT_FAILURE);
    }
 
    output = fopen(argv[2], "a");
    if (output == NULL) {
        perror("File open");
        exit(EXIT_FAILURE);
    }
 
    while (fgets(buffer, BUFSIZE, input) != NULL) {
        fputs(buffer, output);
    }
 
    return 0;
}

Notice that fgets() simply copies text data from input stream into buffer of BUFSIZE and fputs() writes the buffer to output stream until fgets() encounters ‘EOF’ character in the input stream.

advertisement
advertisement

Similarly, we can write a simple C program for Input of text data from stdin and write the same to stdout streams. I leave this for you as an exercise.

Let’s turn to understand formatted I/O of text data using printf and scanf family of functions. scanf family of functions are used to perform input of text data, formatting data according to format specifiers in the format string. Different members in the scanf family differ in where they obtain input from. They are prototyped below,

    int fscanf(FILE *stream, char const *format, ...);
    int scanf(char const *format, ...);
    int sscanf(char *buffer, char const *format, ...);

Notice the ellipsis in each function indicating a variable-length list of pointers. Values converted from from input are stored one-by-one into locations pointed to by these arguments.

The simplest scanf() reads input from stdin, converts values according to format codes and store them into locations specified by corresponding pointers. For ex. (I write just relevent code)

    char cval;
    int ival;
    float fval;
 
    scanf("%d %f %c", &ival, &fval, &cval);

Notice that scanf() reads from stdin three values, converts them according to format codes in the format string and store them into corresponding locations specified by pointers. Further, scanf() returns no. of successfully convered values as functins value. For ex. in the above code fragment, scanf() retuns 3 if all thee were successfully converted. scanf() doesn’t skip over white space characters i.e. it stops reading as soon as it encounters whie space characters/newlines etc.

Let’s attempt to read a string with scanf() below

    char hello[50];
 
    scanf("%s", hello);

What’ll happen if you give input as follows

advertisement
dear brother, how are you these days?

scanf() reads word “dear” before encounters white space and stops. For reading strings we require function from scanf family which skips over white space characters. Rest two functions ignores white space characters.

fscanf() reads from a given stream, converts values according to format codes given in the format string and put them into corresponding locations identified by corresponding pointers. For ex.

/* lo_input1.c -- program uses fscanf() to process line-oriented input */
/* fscanf() skips over whitespaces and even newlines entered */
#include <stdio.h>
#include <stdlib.h>
#define BSIZE 100
 
void processlines(FILE *);
 
int main(void)
{
    printf("**This program reads set of 4 integers in each "
            "input and process them.**\n");
    processlines(stdin);
}
 
void processlines(FILE *input)
{
    int TRUE = 1
    char buffer[BSIZE];
    int a, b, c, d;
 
    /* let's read input */
    /* line by line input can't be performed using fscanf() */
    while (TRUE) {
        printf("user, enter four integers...\n");
        if (fscanf(input, "%d %d %d %d", &a, &b, &c, &d) == 4 ) {
            /* process the values */
            a += 1;
            b += 1;
            c += 1;
            d += 1;
 
            printf("Processed values become: a = %d, b = %d, c = %d, "
                    "d = %d\n", a, b, c, d);
        }
 
       /* reads all characters until encounters newline char */
        while (getchar() != '\n'); 
        printf("want to stop now, enter zero '0' else enter 1: ");
        scanf("%d", &TRUE);
 
    }
}

Output of the above program is outlined below

advertisement
**This program reads set of 4 integers in each input and process them.**
user, enter four integers...
1 2 3 4 5
Processed values become: a = 2, b = 3, c = 4, d = 5
user, enter four integers...
6 7 8 9 10
Processed values become: a = 6, b = 7, c = 8, d = 9
user, enter four integers...
11 12 13 14 15 16 17
Processed values become: a = 10, b = 11, c = 12, d = 13
user, enter four integers...
Processed values become: a = 14, b = 15, c = 16, d = 17
user, enter four integers...

Notice that fscanf() function skips over any white space characters even newlines. Further, line-oriented input can’t be performed with fscanf() because it skips over newlines too. Notice that fscanf() and scanf() functions begin reading from where they left off last time i.e. after having read first four values viz. 1, 2, 3, 4 by fscanf() in the first input, fscan() began to read from where it had left off last time i.e. from value 5 given in the first input and continued to read three more values from second input viz. 6, 7, 8, 9, 10 and continued this way. I added the following construct

    while (getchar() != '\n');

in order to prevent scanf() from reading where fscanf() left off on the input. Then scanf() reads in an integer value entered by user which is tested in while loop to decide whether or not to continue.

Now, we take on sscanf() function. Unlike fscanf(), sscanf() function skips over white spaces but newlines. sscanf() reads from buffer rather than any input stream.

/* lo_input.c -- program uses sscanf() to process input line by line */
/* Though fscanf() skips over whitespaces and even newlines entered */
/* sscanf() doesn't */
#include <stdio.h>
#include <stdlib.h>
#define BSIZE 100
#define TRUE 1
 
void linebyline(FILE *);
 
int main(void)
{
    printf("**This program reads set of 4 integers in each "
           "input and process them.**\n");
    linebyline(stdin);
 
    return 0;
}
 
void linebyline(FILE *input)
{
    char buffer[BSIZE];
    int a, b, c, d, e;
 
    /* let's read input line by line using sscanf() */
    printf("To terminate program, hit ENTER key on the blank line.\n");
    while (fgets(buffer, BSIZE, input) != NULL && buffer[0] != '\n') {
 
        if (sscanf(buffer, "%d %d %d %d %d", &a, &b, &c, &d, &e) != 4 ) {
            fprintf(stderr, "Bad input skipped: %s", buffer);
            continue;
        }
 
        /* process the values */
        a += 1;
        b += 1;
        c += 1;
        d += 1;
 
        printf("Processed values become: a = %d, b = %d, c = %d, "
               "d = %d\n", a, b, c, d);
        printf("To terminate program, hit ENTER key on the blank "
               "line.\n");
     }
    printf("Thank you!\n");
}

Output follows

**This program reads set of 4 integers in each input and process them.**
 
To terminate program, hit ENTER key on the blank line.
1
Bad input skipped: 1
1 2 3 4 5 6
Bad input skipped: 1 2 3 4 5 6
1 2 3 4
Processed values become: a = 2, b = 3, c = 4, d = 5
To terminate program, hit ENTER key on the blank line.
5 6 7 8 9
Bad input skipped: 5 6 7 8 9
12 13 14 15
Processed values become: a = 13, b = 14, c = 15, d = 16
To terminate program, hit ENTER key on the blank line.
 
Thank you!

Notice that sscanf() reads from buffer, stops as soon as it encounters newline character. sscanf() though skips over white spaces but newlines.

Notice that each member in scan family of functions returns no. of successfully converted values as function value. Further, each member stops reading input if EOF is reached or input is read that doesn’t match what the format string specifies. If EOF is reached before any input values have been converted, the function returns the constant EOF. For the functions to work properly, pointer arguments must be the right type fot the corresponding format codes. For ex.

    int ival;
 
    scanf("%d", &ival);

Notice that format code ‘d’ specifies value read to be converted into an integer. What’ll happen if you type in a string as

hello

Take the test and analyse the output.

Let’s consider functions for formatted line output which are prototyped below

    int fprintf(FILE *stream, char const *format, ...);
    int printf(char const *format, ...);
    int sprintf(char *buffer, char const *format, ...);

We are familiar with printf() function which formats values in its argument list
according to format codes.
codes. For ex.

    int a = 10, b = 20;
 
    printf("a is %d, b is %d\n", a, b);

printf() converts values a and b according to format codes in format string. What’ll happen if types of values don’t match the corresponding format codes? Give youself a try!

Rest two members work the same. Notice that printf() writes to stdout stream. fprintf() writes to any stream including stdout while sprintf() writes it’s result as a NUL-terminated string in the specified buffer rather than to a stream.

Notice that sprintf() doesn’t contain buffer_size argument because of which an output that is unexpectedly long will spill over end of the buffer and overwrite whatever happens to follow the buffer in memory. To guard against overflowing, analyse the format to see how long the resulting output would be when the largest possible values are converted.

Another source of error is having arguments whose types don’t match the corresponding format codes. For ex.

    char ch = 'A';
    int cats = 20;
    float size = 13.34;
    char welcome[15] = "Welcome home!";
 
    printf("%c, %d, %f, %s\n", welcome, size, ch, cats);

What do you think about how printf() will result output? Usually, such errors cause garbage in output, but this is also possible that such errors can cause to abort the program.

Sanfoundry Global Education & Learning Series – 1000 C Tutorials.

If you wish to look at all C Tutorials, go to C Tutorials.

advertisement
advertisement
Subscribe to our Newsletters (Subject-wise). Participate in the Sanfoundry Certification contest to get free Certificate of Merit. Join our social networks below and stay updated with latest contests, videos, internships and jobs!

Youtube | Telegram | LinkedIn | Instagram | Facebook | Twitter | Pinterest
Manish Bhojasia - Founder & CTO at Sanfoundry
Manish Bhojasia, a technology veteran with 20+ years @ Cisco & Wipro, is Founder and CTO at Sanfoundry. He lives in Bangalore, and focuses on development of Linux Kernel, SAN Technologies, Advanced C, Data Structures & Alogrithms. Stay connected with him at LinkedIn.

Subscribe to his free Masterclasses at Youtube & discussions at Telegram SanfoundryClasses.