使用 rewind() 和 popen()

问题描述 投票:0回答:2

我有一个简单的C代码片段如下:

#include <stdio.h>
void main() {
  FILE *f;
  char c;
  f = popen("ls", "r");
  while ((c = fgetc(f)) != EOF) {
    //Some tasks
  }
  rewind(f);
  while ((c = fgetc(f)) != EOF) {
    printf("%c", c);
  }
  fclose(f);
}

我不知道为什么代码没有输出任何内容。看来 rewind() 函数不起作用。请帮忙找出我哪里错了。谢谢。

c unix
2个回答
8
投票

rewind
相当于
fseek(stream, 0L, SEEK_SET)
并且
fseek
仅在文件上合法,在流上不合法(管道就是这种情况)。

检查

errno
之后的
rewind
应该是

EBADF 指定的流不是可查找流。


0
投票

答案:使用 memstream 倒带

正如其他人已经说过的,你不能重绕管道。没有说的是一个好的解决方案可能是什么。幸运的是,答案很简单,至少在 UNIX 上,使用 memstream 将数据作为流缓冲到内存中,您可以回滚。

TL;博士

FILE *mem = open_memstream(NULL, NULL); while ((c = fgetc(f)) != EOF) { fputc(c, mem); //...Some tasks... } rewind(mem);
关于内存流

memstream 是内存中动态分配的缓冲区,您可以将其视为文件。它的工作方式与任何其他 C 流一样,因此无需学习新接口。

它长期以来一直是 POSIX 标准的一部分,并且适用于所有现代 UNIX 系统。实现包含在基于 GNU 和 BSD 的系统(例如 Debian 和 MacOS)的 C 库中。

替代品

最简单的答案是,再次打开管道在大多数情况下确实有效,而且很简单。但是,如果不能保证每次数据都相同,那么使用该技术并不是一个好主意。此外,一些程序员可能会觉得编写这种明显次优的代码很烦人。

手动将数据读入数组是一种合理的选择,但在某些需要流的情况下可能不可行,例如使用某些库(例如 libpng)或编程框架(例如 lex 解析)。此外,根据内存的分配方式,数组的编码可能会更复杂。

完整代码

以下是问题中使用 memstream 的示例代码。

#include <stdio.h> #include <err.h> /* For err() */ void main() { FILE *f, *mem; int c; f = popen("ls", "r"); mem = open_memstream(NULL, NULL); if (mem == NULL) err(EXIT_FAILURE, "open_memstream"); while ((c = fgetc(f)) != EOF) { fputc(c, mem); // ... Some tasks ... } pclose(f); rewind(mem); while ((c = fgetc(mem)) != EOF) { printf("%c", c); } fclose(mem); }
缺点

缺点是 memstream 不是 C 标准的一部分。除非您自带实现,否则它可能无法在 Microsoft Windows 中运行。 (请参阅:

MSVC 的 open_memstream 的等效项)。

© www.soinside.com 2019 - 2024. All rights reserved.