代码之家  ›  专栏  ›  技术社区  ›  helpermethod

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

  •  6
  • helpermethod  · 技术社区  · 14 年前

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

    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 回复  |  直到 14 年前
        1
  •  13
  •   Jonathan Leffler    10 年前

    如果你不打算使用 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行结尾,那么从CSV代码中借用一个叶子 "The Practice of Programming" 作者Kernighan&Pike(一本优秀的书)并使用:

    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);
    
        2
  •  1
  •   Steve Emmerson    14 年前

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

    我和小偷在一起,你应该用 fgets() ,而不是。将输入读入比任何合法输入都大的缓冲区,然后检查最后一个字符是否是换行符。

        3
  •  1
  •   pmg    14 年前

    除非您希望获得一种超高效的方法来设置读取的字符数,否则请使用 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兆字节),那么最好逐字读取并保持计数 数字字符 比使用 fgets 第一个和 strlen 稍后。

        4
  •  1
  •   Jonathan Leffler    9 年前

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

     fgets (buffer, BUFFER_SIZE, fp);
    

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

    fgets returns :

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

        5
  •  -1
  •   user411313    14 年前

    没有行大小限制,严格地说是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);
    
        6
  •  -2
  •   Tim Cooper    14 年前
    #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);
    
    }
    
    推荐文章