简体   繁体   English

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

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

I need a version of read line that is memory save. 我需要读取行的版本可以节省内存。 I have this "working" solution. 我有这个“工作”的解决方案。 But I'm not sure how it behaves with memory. 但是我不确定它在内存中的行为。 When I enable free(text) it works for a few lines and then I get an error. 当我启用free(text)它可以工作几行,然后出现错误。 So now neither text nor result is ever freed although I malloc text. 因此,尽管我分配了文本,但现在文本和结果都不会被释放。 Is that correct ? 那是对的吗 ? And why is that so ? 为什么会这样呢?

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

I have A LOT of short lines to read with this and I also need stdin to be replaceable with a FILE* handle. 我有很多短行需要阅读,我还需要stdin可以用FILE*句柄替换。 There is no need for me to realloc text because I have only short lines. 因为我只有几行,所以不需要重新分配文本。

fgets returns a pointer to the string, so after the fgets line, result will be the same memory address as text . fgets返回指向字符串的指针,因此在fgets行之后, result将与text相同。 Then when you call free (text); 然后当您拨打free (text);电话free (text); you are returning invalid memory. 您正在返回无效的内存。

You should free the memory in the calling function when you have finished with result 完成result后,应释放调用函数中的内存

You could also avoid the malloc/free stuff by structuring your code to pass a buffer something like this: 您还可以通过结构化代码以传递类似于以下内容的缓冲区来避免使用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;
}

Trying to avoid the malloc/free is probably wise if you are dealing with many small, short lived items so that the memory doesn't get fragmented and it should faster as well. 如果要处理许多小而短暂的项目,则尽量避免使用malloc / free可能是明智的选择,这样内存就不会碎片化,并且它也应该更快。

char *fgets(char *s, int size, FILE *stream) reads in at most one less than size characters from stream and stores them into the buffer pointed to by s . char *fgets(char *s, int size, FILE *stream) char *fgets(char *s, int size, FILE *stream) 读取最多小于大小的字符,并将其存储到s指向的缓冲区中。 Reading stops after an EOF or a newline. 在EOF或换行符之后停止读取。 If a newline is read, it is stored into the buffer. 如果读取换行符,则将其存储到缓冲区中。 A terminating null byte ( '\\0' ) is stored after the last character in the buffer. 终止空字节( '\\0' )存储在缓冲区中的最后一个字符之后。

Return Value : returns s on success, and NULL on error or when end of file occurs while no characters have been read. 返回值成功则返回s ,错误则返回NULL ,或者在未读取任何字符的情况下返回文件结尾。

So there are 2 critical problems with your code: 因此,您的代码存在2个关键问题:

  1. You don't check the return value of fgets 您无需检查fgets的返回值
  2. You want to deallocate the memory, where this string is stored and return a pointer to this memory. 您要取消分配存储该字符串的内存,并返回指向该内存的指针。 Accessing the memory, where such a pointer (dangling pointer) points to, leads to undefined behaviour . 访问此类指针(悬挂指针)指向的内存会导致未定义的行为

Your function could look like this: 您的函数可能如下所示:

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

and then caller of this function should be responsible for deallocating the memory that return value of this function points to. 然后此函数的调用者应负责释放该函数的返回值指向的内存。

I know you mentioned that the lines are only short, but none of the solutions provided will work for lines greater than 1024 in length. 我知道您提到过,这些行只是短而已,但是所提供的解决方案都不适用于长度大于1024的行。 It is for this reason that I provide a solution which will attempt to read entire lines, and resize the buffer when there's not enough space. 出于这个原因,我提供了一种解决方案,该方案将尝试读取整行,并在空间不足时调整缓冲区的大小。

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

An ideal solution should be able to operate with a fixed buffer of 1 byte. 理想的解决方案应该能够使用1字节的固定缓冲区进行操作。 This requires a more comprehensive understanding of the problem, however. 但是,这需要对该问题有更全面的了解。 Once achieved, adapting such a solution would achieve the most optimal solution. 一旦实现,采用这种解决方案将获得最佳解决方案。

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

Why did no one propose to move the buffer from heap to stack ? 为什么没有人提议将缓冲区从堆移动到堆栈? This is my solution now: 现在这是我的解决方案:

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