C – 如何从Stdin或文件内存中读取字符串行
我需要一个读取行的版本,即内存保存。 我有这个“工作”的解决方案。 但我不确定它与记忆的表现如何。 当我启用free(text)
它适用于几行,然后我收到一个错误。 所以现在虽然我是malloc文本,但是文本和结果都没有被释放。 那是对的吗 ? 为什么会这样呢?
#include #include #include 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);
你正在返回无效的记忆。
完成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
或文件结束时没有读取任何字符。
因此,您的代码存在两个严重问题:
- 您不检查
fgets
的返回值 - 您想要释放存储此字符串的内存并返回指向此内存的指针。 访问指针(悬空指针)指向的内存会导致未定义的行为 。
你的function可能如下所示:
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 #include #include #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 #include #include 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; }