C 中的动态缓冲区 fgets

Dynamic buffer fgets in C

我一直在搜索如何使用 fgets 分配动态缓冲区,但我似乎无法在这个示例中找到它。该文件有两个未知长度的数字,由白色-space 分隔。对于每一行,它读取每个字符,直到 ' '\n 并打印出来。

char *ptr;
char line[MAX];

while(fgets(line, sizeof line , fp) != NULL){
     ptr = line;
     for(i=0; i<2; i++){
        while(*ptr && (*ptr) != ' '){     
             if(*ptr == ' ')
                break;   
             k = (*ptr) - '0';
             if(k != -38)    // wont print '\n'
                printf("%d", k);            
             ptr++;
        }
       while(*ptr && (*ptr) != '\n') {
             if(*ptr == ' '){
                ptr++;
                continue;
             }      
             k = (*ptr) - '0';
             printf("%d", k);       
             ptr++;
     }
   }
}

Can someone give me an idea on how to make line dynamic while still using ptr that way?

您不能在 C 中的 运行 时间内更改数组的大小。这是非法的。这是因为数组是从栈中分配的。要使大小是动态的,您必须声明一个指针,并为其动态分配内存。此数据是从堆中分配的。

您可以使用 realloc 更改分配的内存大小。

int lineLen = 80;
char *line;
line = (char *)malloc(sizeof(char) * 80);
if (line == NULL) {
    // Something went horribly wrong
    exit(1);
}
while (fgets(line, lineLen, fp)) {
    // Do something to find the size
    line = (char *)realloc(line, sizeof(char) * newLen);
    if (line == NULL) {
        // Something went horribly wrong
        exit(1);
    }
}

但是,分配和重新分配内存是一项相当昂贵的操作。因此,如果可以安全地选择大缓冲区大小,您可能会更有效。如果你有一个短循环,那么它可能不足以担心,但不断改变你的缓冲区大小可能是不可取的。

我想你想要的是这样的:

size_t linelen = 80;
char *line = malloc(linelen);

while(magic_reallocating_fgets(&line, &linelen, fp) != NULL) {
    /* ... do whatever you want with line ... */
}

但是,当然,64,000 美元的问题是,magic_reallocating_fgets 是什么样子的?是这样的:

char *magic_reallocating_fgets(char **bufp, size_t *sizep, FILE *fp) {
    size_t len;
    if(fgets(*bufp, *sizep, fp) == NULL) return NULL;
    len = strlen(*bufp);
    while(strchr(*bufp, '\n') == NULL) {
        *sizep += 100;
        *bufp = realloc(*bufp, *sizep);
        if(fgets(*bufp + len, *sizep - len, fp) == NULL) return *bufp;
        len += strlen(*bufp + len);
    }
    return *bufp;
}

那不是真正完整的代码,几乎是伪代码。我留了两件事给你作为练习:

它没有对 mallocrealloc 调用进行错误检查。

它的效率有点低,因为它在读取的每一行上都进行了两次额外的传递:计算字符数,然后再次寻找 '\n'。 (事实证明 fgets 的界面不适合此类工作。)

在支持 glibc >= 2.7 或 POSIX.1-2008 的系统上,我认为你想要的可以使用以下方法实现:

char *line;
while (fscanf(f, "%m[^\n]\n", &line) == 1) {
    /* do stuff with line */
    free(line);
}

这在我的 Linux 系统上运行良好,但在 Windows 世界中,Microsoft Visual C++ 既不支持 %m,也不支持我能找到的任何等效项。