I am learning C, and I finally solved the following exercise, after couple of hours of struggle:
“Write a program that merges lines alternately from two files and writes the results
to stdout. If one file has less lines than the other, the remaining lines from the
larger file should simply be copied to stdout.”
However, I am not satisfied with the code. I feel that I over complicated it, and that there’s
a simpler solution.
How do I improve this code?
#include <stdio.h>
#include <stdbool.h>
int main (void)
{
char file1[11], file2[11];
FILE *input1, *input2;
int c, d, i = 0;
bool end_of_file1 = false, end_of_file2 = false;
bool file1_newline = false, file2_newline = false;
printf ("Enter the name of the two files to be merged,\
separated by space: ");
scanf ("%10s %10s", file1, file2);
input1 = fopen (file1, "r");
input2 = fopen (file2, "r");
while ( end_of_file1 == false ) {
if ( file1_newline == false )
c = getc (input1);
if ( end_of_file2 == true && end_of_file1 == false
&& i == 0 ) {
putc ('\n', stdout);
i = 1;
}
if ( c == '\n' && end_of_file2 == true )
i = 0;
if ( (c == '\n' && file1_newline == false) ||
(c == EOF && file1_newline == false) ) {
file1_newline = true;
putc (' ', stdout);
}
if ( file1_newline == false )
putc (c, stdout);
if ( file1_newline == true )
d = getc (input2);
if ( d == EOF ) {
end_of_file2 = true;
if ( c == EOF )
end_of_file1 = true;
}
if ( file1_newline == true && end_of_file2 == false )
putc (d, stdout);
if ( (d == '\n' && c != EOF) || end_of_file2 == true )
file1_newline = false;
}
fclose (input1);
fclose (input2);
return 0;
}
Algorithmic issues
…Let’s look at the question, instead of the code…
Given that you are supposed to be dealing with lines, it seems better to read whole lines. For that, you should use
fgets()or perhapsgetline()(though the latter is less widely available than the former).Nitpicking style
Personally, I don’t like the way you have spaces around the parentheses on functions – K&R distinguish between operators such as
ifandforwhere there’s a space separating the keyword and the expression and function calls where there is no such space. It is a style issue, though, so very subjective.These lines of code give ample ammunition:
Don’t combine multiple declarations on a single line, especially when they’re initialized.
(But you get plus points for using suffixes 1 and 2 rather than ‘no suffix’ and 2.)
Don’t split string literals across lines with backslashes. That is a very antique way to do it. Use string concatenation, standard since 1989 (and fix the grammar too). Note that among the many defects of the backslash-newline technique are that it screws up the indentation of the code and it is very vulnerable to editing errors.
Consider a
fflush(stdout);before reading. In practice, it isn’t usually necessary, but worth thinking about. Notice that the user can enter the two names on separate lines; that will also work. Limiting the file names to just 10 characters is rather parsimonious, I think; you should probably allow for at least 256 characters. It is good that you specified the size of the strings in format arguments, and did so correctly (atsizeof(array)-1, notsizeof(array)). A more useful design of program would probably take the file names from the command line arguments to the program, rather than prompting the user for the names.Always test the result of
scanf():Always test the result of
fopen():More of your code
Indent the body of your loop by one level (or, on StackOverflow, do not use tabs). You are correct to use
intforc(and laterd).The logic which follows in the loop is … obscure. It is not clear what you are up to. Generally, you want to head off EOF as soon as you can; you wait a while before doing that test. The body of the loop is inscrutable to me – very complex logic (well, it looks complex; I suspect the underlying logic is simple, but
since there’s no explanation of what it does,it looks convoluted).