繁体   English   中英

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

[英]C - How to Read String Lines from Stdin or File Memory Save

我需要读取行的版本可以节省内存。 我有这个“工作”的解决方案。 但是我不确定它在内存中的行为。 当我启用free(text)它可以工作几行,然后出现错误。 因此,尽管我分配了文本,但现在文本和结果都不会被释放。 那是对的吗 ? 为什么会这样呢?

#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*句柄替换。 因为我只有几行,所以不需要重新分配文本。

fgets返回指向字符串的指针,因此在fgets行之后, result将与text相同。 然后当您拨打free (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可能是明智的选择,这样内存就不会碎片化,并且它也应该更快。

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

返回值成功则返回s ,错误则返回NULL ,或者在未读取任何字符的情况下返回文件结尾。

因此,您的代码存在2个关键问题:

  1. 您无需检查fgets的返回值
  2. 您要取消分配存储该字符串的内存,并返回指向该内存的指针。 访问此类指针(悬挂指针)指向的内存会导致未定义的行为

您的函数可能如下所示:

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;
    }
}

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

我知道您提到过,这些行只是短而已,但是所提供的解决方案都不适用于长度大于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字节的固定缓冲区进行操作。 但是,这需要对该问题有更全面的了解。 一旦实现,采用这种解决方案将获得最佳解决方案。

#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);
}

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

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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM