我正在学习C,经过几个小时的努力,我终于解决了以下练习:
“编写一个程序,从两个文件中交替合并行并写入结果 到stdout。如果一个文件的行数少于另一个文件,则剩下的行数来自 应该将较大的文件复制到stdout。“
但是,我对代码不满意。我觉得我过于复杂,而且有 一个更简单的解决方案。
如何改进此代码?
#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;
}
答案 0 :(得分:5)
...让我们看看问题,而不是代码...
编写一个程序,从两个文件交替合并行,并将结果写入
stdout
。如果一个文件的行数少于另一个文件,则较大文件中的其余行应简单地复制到stdout
。
鉴于你应该处理线条,看起来整条线似乎更好。为此,您应该使用fgets()
或getline()
(尽管后者的可用性不如前者)。
char line1[4096];
char line2[4096];
...
char *l1 = fgets(line1, sizeof(line1), input1);
char *l2 = fgets(line2, sizeof(line2), input2);
while (l1 != 0 && l2 != 0)
{
fputs(line1, stdout);
fputs(line2, stdout);
l1 = fgets(line1, sizeof(line1), input1);
l2 = fgets(line2, sizeof(line2), input2);
}
/* One file has reached EOF */
if (l1 != 0)
{
fputs(line1, stdout);
while (fgets(line1, sizeof(line1), input1) != 0)
fputs(line1, stdout);
}
if (l2 != 0)
{
fputs(line2, stdout);
while (fgets(line2, sizeof(line2), input2) != 0)
fputs(line2, stdout);
}
就我个人而言,我不喜欢在函数的括号周围有空格的方式 - K&amp; R区分运算符,例如if
和for
,其中有一个空格分隔关键字和表达式和函数调用没有这样的空间。但这是一个风格问题,因此非常主观。
这些代码行提供了充足的弹药:
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");
不要在一行上组合多个声明,尤其是初始化时。
bool end_of_file1 = false;
bool end_of_file2 = false;
bool file1_newline = false;
bool file2_newline = false;
(但是你使用后缀1和2而不是'无后缀'和2来获得加分。)
不要在带有反斜杠的行之间拆分字符串文字。这是一种非常古老的方式。自1989年以来使用字符串连接,标准(并修复语法)。请注意,在反斜杠换行技术的许多缺陷中,它会破坏代码的缩进,并且非常容易受到编辑错误的影响。
printf("Enter the names of the two files to be merged,"
" separated by space: ");
在阅读之前考虑fflush(stdout);
。在实践中,它通常不是必需的,但值得考虑。请注意,用户可以在单独的行中输入两个名称;那也行。我认为,将文件名限制为10个字符是相当简约的。你应该允许至少256个字符。您可以在格式参数中指定字符串的大小,并且正确执行(在sizeof(array)-1
,而不是sizeof(array)
)。一个更有用的程序设计可能会从程序的命令行参数中获取文件名,而不是提示用户输入名称。
始终测试scanf()
的结果:
if (scanf("%10s %10s", file1, file2) != 2)
...something went wrong...
始终测试fopen()
的结果:
if ((input1 = fopen (file1, "r")) == 0)
...something went wrong...
if ((input2 = fopen (file2, "r")) == 0)
...something went wrong...
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;
}
将循环体缩进一级(或者,在StackOverflow上,不要使用制表符)。对int
(以及之后的c
)d
使用{{1}}是正确的。
循环中的逻辑是......模糊不清。目前尚不清楚你在做什么。一般来说,你想尽快关闭EOF;你在做那个测试之前等了一会儿。循环的主体对我来说是不可理解的 - 非常复杂的逻辑(好吧,它看起来很复杂;我怀疑底层逻辑很简单,但因为没有解释它的作用,它看起来很复杂)。
答案 1 :(得分:1)
== false
和== true
。i
)。if
重新组织(合并,嵌套,排除,排序)为最简单的形式。从最普遍的意义上讲,Karnaugh maps可能有所帮助;无论如何,它通常是在纸上手动完成的。file2_newline
)除此之外(以及其他人给出的其他建议),您可以获得的最佳建议是阅读代码。阅读很多代码,已知是很好的代码。我对FreeBSD tcp/ip stack codebase有很好的体验,还有很多其他很好的例子。
我以为我会尝试将自己的上述建议应用到您的代码中,这就是我所得到的:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define BUFSIZE 10
char buffer[BUFSIZE];
bool append_line_and_check_eof(FILE *input, char newline_replacement);
bool process_line(FILE *to_process, FILE *other, char newline_replacement);
int main(void) {
char file1[11], file2[11];
FILE *input1, *input2;
printf("Enter the name of the two files to be merged, separated by space: \n");
fflush(stdout);
scanf("%10s %10s", file1, file2);
input1 = fopen(file1, "r");
input2 = fopen(file2, "r");
if (!input1 || !input2)
return 1;
while (process_line(input1, input2, ' ') && process_line(input2, input1, 0))
printf("\n");
fclose(input1);
fclose(input2);
return 0;
}
// prints a line from 'to_process', appends all from 'other' if eof is reached.
// returns whether to continue processing or not.
bool process_line(FILE *to_process, FILE *other, char newline_replacement) {
bool eof = append_line_and_check_eof(to_process, newline_replacement);
if (eof) {
/* append rest from 'other' */
while (fgets(buffer, BUFSIZE, other))
printf("%s", buffer);
return false;
}
return true;
}
bool append_line_and_check_eof(FILE *input, char newline_replacement) {
bool newline;
do {
if (!fgets(buffer, BUFSIZE, input))
return true;
/* discriminate between full buffer and eof */
int len = strlen(buffer);
newline = buffer[len - 1] == '\n';
if (newline)
buffer[len - 1] = newline_replacement;
printf("%s", buffer);
} while (!newline);
return false;
}
代码在功能上与您的代码等效,编写了一种希望更好地读取的方式,并遵循我的建议。
答案 2 :(得分:1)
这是另一种方法。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if(argc != 3) return 1;
FILE *fp, *fp2;
char *line, *line2, buf[BUFSIZ];
fp = fopen(argv[1], "r");
if(!fp) {
perror(argv[1]);
return 0;
}
fp2 = fopen(argv[2], "r");
if(!fp2) {
perror(argv[2]);
fclose(fp);
return 0;
}
do {
line = fgets(buf, BUFSIZ, fp);
if(line) printf("%s", line);
line2 = fgets(buf, BUFSIZ, fp2);
if(line2) printf("%s", line2);
} while( line || line2 );
fclose(fp);
fclose(fp2);
return 0;
}