是否有一个很好的替代fgets?

时间:2016-01-09 15:36:16

标签: c string fgets

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

3 个答案:

答案 0 :(得分:4)

#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] = '\0';

    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会覆盖以前的完美数据,导致内存泄漏和数据丢失。

答案 1 :(得分:3)

如果遇到换行符并且fgets返回,则可以根据需要多次再次运行它,以便根据需要读取多行。循环对此很有用。

如果遇到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] = '\0';

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

    printf("%s", result);

    free(result);
    return 0;
}

答案 2 :(得分:1)

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

你也可以反复拨打fgetc ...(顺便说一下,你应该为你确定正好什么是字符串)

在Linux上,您可以使用tty从终端读取可编辑行(即stdin时为GNU readline)。

要阅读某些类型的字符串,您可以使用fscanf,例如%50s%[A-Z]等......

您可以使用fread

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

您可以稍后阅读整行和parse(也许使用sscanf)。您可以阅读多行并在heap memory中构建一些字符串(例如,在拥有它的系统上使用asprintfstrdup)。