我想只读取FILE
对象的缓冲区中已经存在的内容,这样之后缓冲区就会为空(我可以使用sendfile
之类的对文件描述符进行操作的东西)。我想出了这个功能,它似乎适用于我的64位Linux安装:
int readbuf(FILE *stream, char buf[], size_t *size) {
off_t pos = ftello(stream);
if (pos < 0) return -1;
off_t realpos = lseek(fileno(stream), 0, SEEK_CUR);
if (realpos < 0) return -1;
if (pos > realpos) {
errno = EIO;
return -1;
}
size_t bufsize = realpos - pos;
if (bufsize > *size) {
*size = bufsize;
errno = ERANGE;
return -1;
}
*size = bufsize;
if (fread(buf, bufsize, 1, stream) < 1) {
return -1;
}
return 0;
}
现在我想知道,我能假设这在其他符合POSIX的操作系统上工作吗?(在提供所有相关功能的系统上。)
如果底层文件描述符是可查找的(要么是常规文件,要么是块设备,除非你的系统上有其他奇怪的可查找对象…),那么你尝试做什么就没有意义了。只需使用ftello
来获取FILE
中的逻辑位置,然后丢弃FILE
并使用sendfile
。无论如何,在用户空间中使用已经缓冲的数据实际上比sendfile
慢。
如果底层文件描述符不可查找,则整个方法不起作用,因为lseek
将始终返回-1,而ftello
将返回EOF
。这种情况下的潜在解决方案:
- 使用
dup
创建一个新的文件描述符,引用相同的打开文件描述 - 仅打开
/dev/null
写入,dup2
位于FILE
使用的旧文件描述符编号之上 - 从
FILE
读取将成功,直到缓冲区耗尽,然后给出读取错误,因为文件描述符现在指的是不可读的文件 - 在这一点上,您可以直接从第一步中复制的fd中进行读取。您还可以自由使用
fclose
和FILE
对于Unix平台上的可查找文件,您应该能够使用fflush()来协调基于fd的使用和基于FILE*的使用,包括读取。详细信息见http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_01和http://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html.
这是对标准C的扩展(不足为奇)。
我不相信stdio API能保证这在任何系统上都能工作。例如,如果注意到缓冲区为空,它可能会执行预读。
你的"解决方案"至多是一个特定的实现破解。