C - 如何从Stdin或文件内存中读取字符串行

时间:2013-02-13 10:56:07

标签: c string malloc free readline

我需要一个内存保存的读取行版本。我有这个"工作"解。但我不确定它在记忆方面的表现如何。当我启用free(text)时,它适用于几行,然后我收到错误。所以现在虽然我是malloc文本,但是文本和结果都没有被释放。那是对的吗 ?为什么会这样呢?

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

char* readFromIn()
{
    char* text = malloc(1024);
    char* result = fgets(text, 1024, stdin);
    if (result[strlen(result) - 1] == 10)
        result[strlen(result) - 1] = 0;
    //free(text);
    return result;
}

我有很多的短线要阅读,我还需要stdin可以用FILE*句柄替换。我没有必要重新分配文本,因为我只有短线。

5 个答案:

答案 0 :(得分:2)

fgets返回指向字符串的指针,因此在fgets行之后,result将与text的内存地址相同。然后,当您致电free (text);时,您将返回无效内存。

完成result

后,您应该释放调用函数中的内存

你也可以通过构造你的代码来传递一个像这样的缓冲区来避免malloc / free东西:

void parent_function ()
{
    char *buffer[1024];

    while (readFromIn(buffer)) {
        // Process the contents of buffer
    }
}

char *readFromIn(char *buffer)
{
    char *result = fgets(buffer, 1024, stdin);
    int len;

    // fgets returns NULL on error of end of input,
    // in which case buffer contents will be undefined
    if (result == NULL) {
        return NULL;
    }

    len = strlen (buffer);
    if (len == 0) {
        return NULL;
    }

    if (buffer[len - 1] == '\n') {
        buffer[len - 1] = 0;

    return buffer;
}

如果你正在处理许多小的,短命的项目,那么试图避免malloc / free可能是明智的,这样内存就不会碎片化,而且它也应该更快。

答案 1 :(得分:1)

char *fgets(char *s, int size, FILE *stream) 从流中读取最多一个小于大小的字符,并将它们存储到s指向的缓冲区中。读数在EOF或换行符后停止。如果读取换行符,则将其存储到缓冲区中。终止空字节('\0')存储在缓冲区中的最后一个字符之后。

返回值成功时返回s,错误时返回NULL,或者在没有读取任何字符时发生文件结束。

因此,您的代码存在两个严重问题:

  1. 您不检查fgets
  2. 的返回值
  3. 您想要释放存储此字符串的内存并返回指向此内存的指针。访问内存,这样的指针(悬空指针)指向,导致未定义的行为
  4. 您的功能可能如下所示:

    public char* readFromIn() {
        char* text = malloc(1024);
        if (fgets(text, 1024, stdin) != NULL) {
            int textLen = strlen(text);
            if (textLen > 0 && text[textLen - 1] == '\n')
                text[textLen - 1] == '\0';     // getting rid of newline character
            return text;
        }
        else {
            free(text);
            return NULL;
        }
    }
    

    然后该函数的调用者应该负责释放该函数返回值指向的内存。

答案 2 :(得分:1)

我知道你提到线路只是短路,但是所提供的解决方案都不适用于长度大于1024的线路。正是出于这个原因,我提供了一种解决方案,它将尝试读取整行,并在没有足够空间时调整缓冲区大小。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MINIMUM_CAPACITY 16

size_t read_line(char **buffer, size_t *capacity) {
    char *buf = *buffer;
    size_t cap = *capacity, pos = 0;

    if (cap < MINIMUM_CAPACITY) { cap = MINIMUM_CAPACITY; }

    for (;;) {
        buf = realloc(buf, cap);
        if (buf == NULL) { return pos; }
        *buffer = buf;
        *capacity = cap;

        if (fgets(buf + pos, cap - pos, stdin) == NULL) {
            break;
        }

        pos += strcspn(buf + pos, "\n");
        if (buf[pos] == '\n') {
            break;
        }

        cap *= 2;
    }

    return pos;
}

int main(void) {
    char *line = NULL;
    size_t size = 0;

    for (size_t end = read_line(&line, &size); line[end] == '\n'; end = read_line(&line, &size)) {
        line[end] = '\0'; // trim '\n' off the end
        // process contents of buffer here
    }

    free(line);
    return 0;
}

理想的解决方案应该能够使用1字节的固定缓冲区运行。然而,这需要更全面地理解该问题。一旦实现,适应这样的解决方案将实现最佳解决方案。

答案 3 :(得分:0)

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

char *readFromIn(FILE *fp)
{
    char text[1024];
    size_t len;

    if (!fgets(text, sizeof text, fp)) return NULL;
    len = strlen(text);

    while (len && text[len-1] == '\n') text[--len] = 0;

    return strdup(text);
}

答案 4 :(得分:0)

为什么没有人建议将缓冲区从堆移到堆栈?这是我现在的解决方案:

char input[1024]; // held ready as buffer for fgets

char* readFromIn()
{
    char* result = fgets(input, 1024, stdin);
    if (result == null)
        return "";
    if (result[strlen(result) - 1] == '\n')
        result[strlen(result) - 1] = 0;
    return result;
}