在 C 中没有可移植的、符合标准的方法来提前知道如何从FILE 流中读取字节。
首先,流甚至可能是不可搜索的——它可以是管道或终端,甚至是套接字连接。在这样的流上,一旦你读取了输入,它就消失了,再也不会被读取了。您可以推回一个char 值,但这还不足以知道还有多少数据需要读取,或者重新读取整个流。
即使流是您可以查找的文件,您也不能在可移植、严格符合 C 代码中使用 fseek()/ftell() 来了解文件的大小。
如果是二进制流,则不能使用 fseek() 查找文件末尾 - 这是明确未定义的行为 per the C standard:
...二进制流不需要有意义地支持 fseek 调用的 wherece 值为SEEK_END。
Footnote 268 even says:
将文件位置指示器设置为文件结尾,与fseek(file, 0, SEEK_END) 一样,对于二进制流具有未定义的行为...
所以你不能在二进制流中便携地使用fseek()。
您不能使用ftell() 来获取文本流的字节数。每the C standard again:
对于文本流,其文件位置指示符包含未指定的信息,fseek 函数可使用该信息将流的文件位置指示符返回到 ftell 调用时的位置;两个这样的返回值之间的差异不一定是衡量写入或读取字符数的有意义的量度。
确实存在从ftell() 返回的值与字节数完全不同的系统。
了解您可以从流中读取多少字节的唯一可移植且一致的方法是实际读取它们,并且您不能依赖于能够再次读取它们。
如果要将整个流读入内存,则必须不断地重新分配内存,或者使用其他一些动态方案。
这是一种将流的全部内容读入内存的非常低效但可移植且严格符合的方式(为了算法清晰和防止出现垂直滚动条,所有错误检查和头文件都被省略 - 它确实需要错误检查并需要正确的头文件):
// get input stream with `fopen()` or some other manner
FILE *input = ...
size_t count = 0;
char *data = NULL;
for ( ;; )
{
int c = fgetc( input );
if ( c == EOF )
{
break;
}
data = realloc( data, count + 1 );
data[ count ] = c;
count++;
}
// optional - terminate the data with a '\0'
// to treat the data as a C-style string
data = realloc( data, count + 1 );
data[ count ] = '\0';
count++;
无论流是什么,这都会起作用。
在 Linux 等 POSIX 风格的系统上,您可以使用 fileno() 和 fstat() 来获取文件的大小(同样,所有错误检查和头文件都被省略了):
char *data = NULL;
FILE *input = ...
int fd = fileno( input );
struct stat sb;
fstat( fd, &sb );
if ( S_ISREG( sb.st_mode ) )
{
// sb.st_size + 1 for C-style string
char *data = malloc( sb.st_size + 1 );
data[ sb.st_size ] = '\0';
}
// now if data is not NULL you can read into the buffer data points to
// if data is NULL, see above code to read char-by-char
// this tries to read the entire stream in one call to fread()
// there are a lot of other ways to do this
size_t totalRead = 0;
while ( totalRead < sb.st_size )
{
size_t bytesRead = fread( data + totalRead, 1, sb.st_size - totalRead, input );
totalRead += bytesRead;
}
以上内容也可以在 Windows 上运行。您可能会得到some compiler warnings,或者也必须使用_fileno()、_fstat() and struct _stat。*
您可能还需要define the S_ISREG() macro on Windows:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
* 那是 _fileno()、_fstat() 和 struct _stat,没有超链接 underline-munge。