使用fgetc()逐行读取c文件

我就是这样做的,但我不确定这是首选的习语:

FILE *fp = fopen(argv[0], "r"); // handle fopen() returning NULL while (!feof(fp)) { char buffer[80]; // statically allocated, may replace this later with some more sophisticated approach int num_chars = 0; for (int ch = fgetc(fp); ch != EOF && ch != '\n'; ch = fgetc()) { buffer[num_chars++] = ch; } // null-terminate the string buffer[num_chars] = '\0'; printf("%s\n", buffer); } 

这没关系,有什么建议可以改善吗?

如果您不打算使用fgets() (可能是因为您想要删除换行符,或者您想要处理"\r""\n""\r\n"行结尾,或者您想知道读取了多少个字符),您可以将其用作骨架函数:

 int get_line(FILE *fp, char *buffer, size_t buflen) { char *end = buffer + buflen - 1; /* Allow space for null terminator */ char *dst = buffer; int c; while ((c = getc(fp)) != EOF && c != '\n' && dst < end) *dst++ = c; *dst = '\0'; return((c == EOF && dst == buffer) ? EOF : dst - buffer); } 

它只识别换行作为行尾; 它删除了换行符。 它不会溢出缓冲区; 它不会丢弃多余的字符,所以如果要求读取一个很长的行,它将以块的forms读取该行; 它返回读取的字符数。 如果你需要区分溢出和恰好是缓冲区长度的行 - 1,那么你可能需要保留换行符 - 代码中的相应变化:

 int get_line(FILE *fp, char *buffer, size_t buflen) { char *end = buffer + buflen - 1; /* Allow space for null terminator */ char *dst = buffer; int c; while ((c = getc(fp)) != EOF && dst < end) { if ((*dst++ = c) == '\n') break; } *dst = '\0'; return((c == EOF && dst == buffer) ? EOF : dst - buffer); } 

在这方面有无穷无尽的小变体,例如,如果必须截断该行,则丢弃任何多余的字符。 如果你想处理DOS,(旧的)Mac或Unix行结尾,那么从Kernighan&Pike的“编程实践”中借用CSV代码(一本优秀的书)并使用:

 static int endofline(FILE *ifp, int c) { int eol = (c == '\r' || c == '\n'); if (c == '\r') { c = getc(ifp); if (c != '\n' && c != EOF) ungetc(c, ifp); } return(eol); } 

然后你可以使用它代替c != '\n'测试:

 int get_line(FILE *fp, char *buffer, size_t buflen) { char *end = buffer + buflen - 1; /* Allow space for null terminator */ char *dst = buffer; int c; while ((c = getc(fp)) != EOF && !endofline(fp, c) && dst < end) *dst++ = c; *dst = '\0'; return((c == EOF && dst == buffer) ? EOF : dst - buffer); } 

处理整个过程的另一种方法是使用fread()fwrite()

 void copy_file(FILE *in, FILE *out) { char buffer[4096]; size_t nbytes; while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), in)) != 0) { if (fwrite(buffer, sizeof(char), nbytes, out) != nbytes) err_error("Failed to write %zu bytes\n", nbytes); } } 

在上下文中,您打开文件并检查其有效性,然后调用:

 copy_file(fp, stdout); 

如果用户输入80个字符或更多字符,则存在缓冲区溢出的风险。

我和ThiefMaster在一起:你应该使用fgets() 。 将输入读入比任何合法输入更大的缓冲区,然后检查最后一个字符是换行符。

除非您希望以超高效的方式设置读取的字符数,否则请使用fgets()

用类似但不同的简单fgets()替换你的例子,你“丢失”了num_chars变量。

 fgets(buffer, sizeof buffer, stdin); fputs(buffer, stdout); /* buffer contains a '\n' */ 

如果你需要删除最后一个’\ n’

 buffer[0] = 0; if (!fgets(buffer, sizeof buffer, stdin)) /* error or eof */; num_chars = strlen(buffer); if (num_chars && (buffer[num_chars - 1] == '\n')) buffer[--num_chars] = 0; puts(buffer); /* add a '\n' to output */ 

如果字符串非常庞大(比如42兆字节),那么你可能最好逐个字符地读取并使用num_chars保持计数,而不是先使用fgetsstrlen

如果您需要每个字符来检查或修改或其他任何其他使用fgets。 其他一切,请使用fgets。

  fgets (buffer, BUFFER_SIZE, fp); 

请注意,fgets将读取,直到达到新行或EOF(或缓冲区已满)。 如果从文件中读取,则新行字符“\ n”也会附加到字符串中。 还附加空字符。

fgets回报:

成功时,该函数返回相同的str参数。
如果遇到文件结尾且未读取任何字符,则str的内容保持不变,并返回空指针。
如果发生错误,则返回空指针。
使用ferror或feof检查是否发生错误或是否已达到文件结尾。

没有lineize-limit和严格的C89(你的代码只有C99)像:

 FILE *fp = fopen(argv[0], "r"); size_t len=1; char c, *buffer=calloc(1,1); /* handle fopen() returning NULL*/ while( c=fgetc(fp),!feof(fp) ) if( c=='\n' ) { puts(buffer); len=1; *buffer=0; } else strncat(buffer=realloc(buffer,++len),&c,1); /* check for NULL needed */ puts(buffer); free(buffer); fclose(fp); 
 #include void main() { FILE *fp; char c; int ch=0,w=0,l=0; fp=fopen("c:\read.txt","w"); clrscr(); if(fp==NULL) { printf("\n\n\tDOES NOT EXIXST"); getch(); exit(0); } while(!feof(fp)) { c=fgetc(fp); ch++; if(c==' ') { w++; } if(c=='\n') { l++; w++; } } printf("\n\n\tTOTAL CHAR = %d\n\n\tTOTAL WORDS = %d\n\n\tTOTAL LINES = %d",ch,w,l); }