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

时间:2010-11-27 19:30:06

标签: c file-io

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

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);
}

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

6 个答案:

答案 0 :(得分:13)

如果您不打算使用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);
}

它只识别换行符作为行尾;它放弃了换行符。它不会溢出缓冲区;它不会丢弃多余的字符,所以如果要求读取很长的行,它将以块的形式读取行;它返回读取的字符数。如果你需要区分溢出和恰好是缓冲区长度的行 - 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&amp ;;的"The Practice of Programming"中的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);

答案 1 :(得分:1)

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

 fgets (buffer, BUFFER_SIZE, fp);

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

fgets returns :

  

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

除非您希望以超高效的方式设置读取的字符数,否则请使用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计算,而不是先使用fgets然后再使用strlen

答案 4 :(得分:-1)

没有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);

答案 5 :(得分:-2)

#include<stdio.h>
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);

}