为何_popen创建的管道文件ftell返回0但fread_s仍能读取内容?
为什么_popen返回的FILE指针用ftell得到0,但fread_s能读到全部内容?
这问题我之前也踩过坑,核心原因是**_popen创建的是管道流,不是普通的磁盘文件**,这类流式设备完全不支持随机访问操作,fseek和ftell在这里的行为根本不可靠。
具体拆解一下:
- 首先,
_popen("dir", "rt")启动dir命令后,子进程的输出是通过匿名管道传递给你的程序的。管道是一种顺序流式设备——它没有固定的"文件大小",数据是子进程一边生成、管道一边传输的,完全不支持SEEK_END这种定位到末尾的操作。 - 你调用
fseek(dir, 0, SEEK_END)的时候,虽然可能返回0(看起来调用成功了),但这个操作实际上没有任何效果,管道的读写位置根本没动。所以后续ftell(dir)返回0也就不奇怪了,因为它没法获取一个流式设备的总长度。 - 而
fread_s能读到预期内容,是因为它是顺序读取的:只要管道里有子进程输出的数据,它就会一直读,直到子进程结束、管道里的数据全部读完为止,完全不需要依赖文件长度或者定位操作。
如果你的需求是获取dir命令的全部输出内容,正确的做法是循环调用fread_s,直到它返回0(表示没有更多数据),把每次读到的内容拼接起来,而不是用fseek+ftell来预分配内存或者获取长度。举个简单的实现例子:
char buffer[1024]; size_t bytesRead; std::string output; while ((bytesRead = fread_s(buffer, sizeof(buffer), sizeof(char), sizeof(buffer)/sizeof(char), dir)) > 0) { output.append(buffer, bytesRead); } // 现在output里就是完整的dir命令输出
最后补充个小提醒:除了管道,像标准输入、标准输出这类控制台流,也都是非可定位的流式设备,同样不能用fseek和ftell来获取长度或者定位。
内容的提问来源于stack exchange,提问作者HellDryx
相关产品推荐
相关产品推荐

