Line Input and Output in C
After having seen the formatted and character Input and Output functions, let's now see how to perform line Input and Output operations in C language.
When we talk about line Input and Output, we refer to reading and writing entire lines of text, rather than single characters or formatted values. In C, this type of operation is usually performed using the fgets() function for input and fputs() for output.
Unlike character I/O, which is able to read or write a single byte at a time and which is suitable for both text files and binary files, line I/O is specifically designed to handle lines of text, making it ideal for text files structured in lines.
- The
fgets()andfputs()functions are used to read and write entire lines of text in C language. - Line I/O is particularly useful for managing text files structured in lines.
fgets()reads a line of text from a file or from standard input, whilefputs()writes a line of text to a file or to standard output.- These functions are more general and safer versions compared to their counterparts
gets()andputs(), which work only with standard input and output respectively.
Line Output Functions: fputs and puts
The fputs() and puts() functions are used to write text strings to a file or to standard output (console).
In particular, their signature is as follows:
int fputs(const char *str, FILE *stream);
int puts(const char *str);
We have already encountered the puts() function previously, to write strings to the console. It writes the string str followed by a newline character (\n) to standard output.
For example, the following code uses puts() to print a string to the console:
#include <stdio.h>
int main() {
puts("Hello, world!");
return 0;
}
An important characteristic of puts() is that it automatically adds a newline character at the end of the string, so it is not necessary to include it manually.
The fputs() function, instead, is a more general version that allows you to specify the destination file through the stream parameter. For example, the program above can be rewritten using fputs() in this way:
#include <stdio.h>
int main() {
fputs("Hello, world!\n", stdout);
return 0;
}
There is, however, an important difference between the two functions: fputs() does not automatically add a newline character at the end of the string, so it is necessary to include it manually if you want the string to be followed by a new line.
Both functions return an integer value that indicates the success or failure of the operation. In case of success, both functions return a non-negative value, while in case of error they return EOF.
Therefore, to verify if the write operation was successful, you can use a check like the following:
if (fputs("Hello, world!\n", stdout) == EOF) {
// Error handling
}
Line Input Functions: fgets and gets
The fgets() and gets() functions are used to read text strings from a file or from standard input (console).
In particular, their signature is as follows:
char *fgets(char *str, int n, FILE *stream);
char *gets(char *str);
The gets() function has also been seen previously, to read strings from the console. It reads a line of text from standard input and stores it in the string str, terminating the reading when a newline character (\n) is encountered or when the end of file (EOF) is reached.
Note that the newline character is not included in the stored string, but is replaced by the string termination character (\0).
An example of using gets() is the following:
#include <stdio.h>
int main() {
char buffer[100];
printf("Enter a line of text: ");
gets(buffer);
printf("You entered: %s\n", buffer);
return 0;
}
The gets function has, however, a serious security flaw: it does not perform any check on the length of the read string, which can lead to buffer overflow if the input exceeds the buffer capacity. For this reason, the use of gets() is strongly discouraged and the function has been removed from the C11 standard.
In its place, there is fortunately the fgets() function, which can be considered as a more general and safer version of gets(). The fgets() function reads up to n-1 characters from a file specified by the stream parameter and stores the resulting string in str. The reading terminates when a newline character is encountered, when the end of file (EOF) is reached, or when n-1 characters have been read.
The example above can be rewritten using fgets() in this way:
#include <stdio.h>
int main() {
char buffer[100];
printf("Enter a line of text: ");
fgets(buffer, sizeof(buffer), stdin);
printf("You entered: %s", buffer);
return 0;
}
Another important difference is that fgets() includes the newline character in the stored string, if present, while gets() replaces it with the string termination character.
Both functions return a pointer to the read string in case of success, so essentially they return a pointer to the buffer str. In case of error or if the end of file is reached before reading any character, both functions return NULL. At this point, you can use the feof() and ferror() functions to determine whether the reading terminated due to the end of file or due to an error.
Example
Let's try to put together what we have seen so far in a simple program that reads a text file one line at a time, reverses the order of the lines, and writes them to another text file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 256
void reverse_line(char *source, char *destination) {
int len = strlen(source);
for (int i = 0; i < len; i++) {
destination[i] = source[len - i - 1];
}
destination[len] = '\0';
}
void remove_newline(char *str) {
size_t len = strlen(str);
if (len > 0 && str[len - 1] == '\n') {
str[len - 1] = '\0';
}
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <input_file> <output_file>\n", argv[0]);
return 1;
}
FILE *input_file = fopen(argv[1], "r");
if (input_file == NULL) {
fprintf(stderr, "Error opening input file");
return 1;
}
FILE *output_file = fopen(argv[2], "w");
if (output_file == NULL) {
fprintf(stderr, "Error opening output file");
fclose(input_file);
return 1;
}
char buffer[MAX_LINE_LENGTH];
char reversed_line[MAX_LINE_LENGTH];
while (fgets(buffer, sizeof(buffer), input_file) != NULL) {
remove_newline(buffer);
reverse_line(buffer, reversed_line);
fputs(reversed_line, output_file);
fputc('\n', output_file);
}
fclose(input_file);
fclose(output_file);
return 0;
}
In this example we have created two support functions: reverse_line() to reverse the order of characters in a line and remove_newline() to remove the newline character at the end of the read line.
The main program opens the input file in read mode and the output file in write mode. Then, it reads each line from the input file using fgets(), removes the newline character, reverses the line and writes it to the output file using fputs(), manually adding a newline character with fputc().
If we have an input file called input.txt with the following content:
Hello how are you?
All good?
See you soon!
Running the program with:
./reverse input.txt output.txt
The output file output.txt will contain:
?uoy era woh olleH
?doog llA
!noos uoy eeS