代码之家  ›  专栏  ›  技术社区  ›  Gary Willoughby

在C中将文本文件读入缓冲区的正确方法[[副本]

  •  23
  • Gary Willoughby  · 技术社区  · 16 年前

    我处理的是一些小文本文件,我希望在处理它们时将其读入缓冲区,因此我想出了以下代码:

    ...
    char source[1000000];
    
    FILE *fp = fopen("TheFile.txt", "r");
    if(fp != NULL)
    {
        while((symbol = getc(fp)) != EOF)
        {
            strcat(source, &symbol);
        }
        fclose(fp);
    }
    ...
    

    这是将文件内容放入缓冲区的正确方法还是我滥用了 strcat() ?

    然后,我遍历缓冲区,因此:

    for(int x = 0; (c = source[x]) != '\0'; x++)
    {
        //Process chars
    }
    
    8 回复  |  直到 16 年前
        1
  •  79
  •   Michael    9 年前
    char source[1000000];
    
    FILE *fp = fopen("TheFile.txt", "r");
    if(fp != NULL)
    {
        while((symbol = getc(fp)) != EOF)
        {
            strcat(source, &symbol);
        }
        fclose(fp);
    }
    

    此代码有很多错误:

    1. 它非常慢(一次提取一个字符的缓冲区)。
    2. sizeof(source) ,这很容易导致缓冲区溢出。
    3. 实际上,当您更仔细地观察它时,这段代码应该根本不起作用。如手册页所述:

    这个 strcat() 函数将以null结尾的字符串s2的副本追加到以null结尾的字符串s1的末尾,然后添加一个以null结尾的“\0”。

    您正在追加一个字符(不是以NUL结尾的字符串!)可以或不可以NUL终止的字符串。这个 只有 我可以想象,根据手册页的描述,如果文件中的每个字符都以NUL结尾,这种情况下,这将是毫无意义的。所以是的,这绝对是一种可怕的虐待 strcat() .

    如果您提前知道最大缓冲区大小:

    #include <stdio.h>
    #define MAXBUFLEN 1000000
    
    char source[MAXBUFLEN + 1];
    FILE *fp = fopen("foo.txt", "r");
    if (fp != NULL) {
        size_t newLen = fread(source, sizeof(char), MAXBUFLEN, fp);
        if ( ferror( fp ) != 0 ) {
            fputs("Error reading file", stderr);
        } else {
            source[newLen++] = '\0'; /* Just to be safe. */
        }
    
        fclose(fp);
    }
    

    #include <stdio.h>
    #include <stdlib.h>
    
    char *source = NULL;
    FILE *fp = fopen("foo.txt", "r");
    if (fp != NULL) {
        /* Go to the end of the file. */
        if (fseek(fp, 0L, SEEK_END) == 0) {
            /* Get the size of the file. */
            long bufsize = ftell(fp);
            if (bufsize == -1) { /* Error */ }
    
            /* Allocate our buffer to that size. */
            source = malloc(sizeof(char) * (bufsize + 1));
    
            /* Go back to the start of the file. */
            if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }
    
            /* Read the entire file into memory. */
            size_t newLen = fread(source, sizeof(char), bufsize, fp);
            if ( ferror( fp ) != 0 ) {
                fputs("Error reading file", stderr);
            } else {
                source[newLen++] = '\0'; /* Just to be safe. */
            }
        }
        fclose(fp);
    }
    
    free(source); /* Don't forget to call free() later! */
    
        2
  •  5
  •   Martin Beckett    16 年前

    是的-你可能会因为你可恶的滥用strcat而被捕!

    看看getline(),它一次读取一行数据,但重要的是它可以限制读取的字符数,这样就不会使缓冲区溢出。

    您通常会保留一个指向字符串存储的当前端的指针,并将其传递给getline,作为读取下一行的位置。

        3
  •  2
  •   toweleeele    10 年前

    如果您使用的是linux系统,那么一旦拥有了文件描述符,就可以使用fstat()获得有关该文件的大量信息

    http://linux.die.net/man/2/stat

    所以你可能有

    #include  <unistd.h> 
    void main()
    {
        struct stat stat;
        int fd;
        //get file descriptor
        fstat(fd, &stat);
        //the size of the file is now in stat.st_size
    }
    

    这样可以避免查找文件的开头和结尾。

        4
  •  1
  •   Mark Ransom    16 年前

    看见 this article from JoelOnSoftware 为什么你不想使用 strcat .

    看看 fread 另一种选择。在读取字节或字符时,将其与1一起用作大小。

        5
  •  1
  •   Martin Wickman    16 年前

    你为什么不直接使用你拥有的字符数组呢?这应该做到:

       source[i] = getc(fp); 
       i++;
    
        6
  •  1
  •   Earlz    16 年前

    未经测试,但应能正常工作。。是的,它可以更好地用fread实现,我将把它作为练习留给读者。

    #define DEFAULT_SIZE 100
    #define STEP_SIZE 100
    
    char *buffer[DEFAULT_SIZE];
    size_t buffer_sz=DEFAULT_SIZE;
    size_t i=0;
    while(!feof(fp)){
      buffer[i]=fgetc(fp);
      i++;
      if(i>=buffer_sz){
        buffer_sz+=STEP_SIZE;
        void *tmp=buffer;
        buffer=realloc(buffer,buffer_sz);
        if(buffer==null){ free(tmp); exit(1);} //ensure we don't have a memory leak
      }
    }
    buffer[i]=0;
    
        7
  •  0
  •   wprl    16 年前
        8
  •  -1
  •   Ioan    16 年前

    你考虑过mmap()吗?您可以直接读取文件,就好像它已经在内存中一样。

    http://beej.us/guide/bgipc/output/html/multipage/mmap.html

    推荐文章