fgets 有好的替代品吗?

Is there a good alternative to fgets?

我只是一名年轻的计算机科学专业的学生,​​目前我对从 stdin 读取字符串的最佳做法是什么有点困惑。我知道有很多方法可以做到这一点,有些方法比其他方法更安全,等等...... 我目前需要一个函数来防止缓冲区溢出并将空终止符 (\0) 附加到字符串的末尾。我发现 fgets 对此非常有用,但是......它停止读取 \n 或 EOF!如果我希望用户一次输入多行怎么办?还有其他功能可以帮助我做到这一点吗? 如果这个问题对你们中的某些人来说似乎很愚蠢,我很抱歉,但是请理解我! 任何帮助将不胜感激。

#define INITALLOC  16  /* #chars initally alloced */
#define STEP        8  /* #chars to realloc by */

#define END       (-1)  /* returned by getline to indicate EOF */
#define ALLOCFAIL    0  /* returned by getline to indicate allocation failure */
int getline(char **dynline)
{
    int i, c;
    size_t nalloced;  /* #chars currently alloced */

    if ((*dynline = malloc(INITALLOC)) == NULL)
        return ALLOCFAIL;

    nalloced = INITALLOC;
    for (i = 0; (c = getchar()) != EOF; ++i) {
        /* buffer is full, request more memory */
        if (i == nalloced)
            if ((*dynline = realloc(*dynline, nalloced += STEP)) == NULL)
                return ALLOCFAIL;

        /* store the newly read character */
        (*dynline)[i] = c;
    }
    /* zero terminate the string */
    (*dynline)[i] = '[=10=]';

    if (c == EOF)
        return END;
    return i+1;  /* on success, return #chars read successfully 
                    (i is an index, so add 1 to make it a count */
}

此函数动态分配内存,因此调用者需要free内存。

这段代码并不完美。如果在重新分配时出现故障,NULL 会覆盖之前的 perfectly-good 数据,从而导致内存泄漏和数据丢失。

如果遇到换行符并且 fgets returns,您可以根据需要多次 运行 以读取任意多行。循环对此很有用。

如果遇到 EOF,则说明您已到达文件 (/stream) 的末尾,运行再次打开它是没有意义的,因为没有什么可读的了。

下面的示例显示了从 stdin 读取整个字符串到 EOF 的逻辑。

有很多方法可以做到这一点,这只是其中一种,但它显示了一般逻辑。

结果缓冲区随着输入的读取而增长,并且没有限制 - 所以如果永远不会达到 EOF,您最终将 运行 内存不足并且程序将退出。一个简单的检查可以避免这种情况,或者根据您的应用程序,您可以在数据传入时对其进行处理,而不需要将其全部存储。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define LINE_BUFFER_SIZE 256

// Each time this is exhausted, the buffer will be increased in size by this amount again.
#define INITIAL_BUFFER_SIZE 2048

int main (int argc, char **argv) {
    char *result = malloc(INITIAL_BUFFER_SIZE);
    if (!result) {
        // Out of memory.
        return 1;
    }

    size_t totalBytesRead = 0;
    size_t bytesAllocated = INITIAL_BUFFER_SIZE;

    char buf[LINE_BUFFER_SIZE];
    while (fgets(buf, LINE_BUFFER_SIZE, stdin)) {
        size_t bytesRead = strlen(buf);
        size_t bytesNeeded = totalBytesRead + bytesRead + 1;
        if (bytesAllocated < bytesNeeded) {
            char *newPtr = realloc(result, bytesAllocated + INITIAL_BUFFER_SIZE);
            if (newPtr) {
                result = newPtr;
                bytesAllocated += INITIAL_BUFFER_SIZE;
            }
            else {
                // Out of memory.
                free(result);
                return 1;
            }
        }

        memcpy(result + totalBytesRead, buf, bytesRead);
        totalBytesRead += bytesRead;
    }
    result[totalBytesRead] = '[=10=]';

    // result contains the entire contents from stdin until EOF.

    printf("%s", result);

    free(result);
    return 0;
}

在 POSIX 系统上,您有 getline。它能够在堆分配的内存中读取任意宽的行(直到耗尽资源)。

您也可以重复调用 fgetc ...(顺便说一句,您应该 确切地定义 什么是适合您的字符串)

在 Linux 上,您可以从终端读取 可编辑的 行(即 stdintty) using GNU readline

要读取 一些 类型的字符串,您可以将 fscanf 与例如%50s%[A-Z] 等...

并且您可以使用 fread

读取数组(字节或其他一些二进制数据)

您可能会从他们那里阅读整行和 parse it later (perhaps using sscanf). You could read several lines and build some strings in heap memory (e.g. using asprintf or strdup 在具有它的系统上)。