我有以下惯用代码段,用于获取二进制文件的长度:
fseek(my_file, 0, SEEK_END);
const size_t file_size = ftell(my_file);
...我知道,要成为书呆子 fseek(file, 0, SEEK_END)
对于二进制流具有不确定的行为 [1] –但坦率地说,在存在此问题的平台上没有fstat()
,无论如何这是另一个问题的话题……
我的问题是:在这种情况下是否应该检查fseek()
的返回值?
if (fseek(my_file, 0, SEEK_END)) {
return 1;
}
const size_t file_size = ftell(my_file);
我从未见过fseek()
在这种情况下被检查过,而且我还想知道fseek()
可能会返回哪种错误。
编辑:
阅读Clifford's answer之后,我还认为在计算文件大小时处理fseek()
和ftell()
返回值的最佳方法是编写一个专用函数。但是Clifford的好建议无法处理size_t
数据类型(毕竟我们需要 size !),所以我想最后最实用的方法是使用指针用于存储文件的大小,并仅在失败时保留我们专用函数的返回值。这是我对克利福德的安全尺寸计算器解决方案的贡献:
int fsize (FILE * const file, size_t * const size) {
long int ftell_retval;
if (fseek(file, 0, SEEK_END) || (ftell_retval = ftell(file)) < 0) {
/* Error */
*size = 0;
return 1;
}
*size = (size_t) ftell_retval;
return 0;
}
这样,当我们需要知道文件的长度时,我们可以简单地做到:
size_t file_size;
if (fsize(my_file, &file_size)) {
fprintf(stderr, "Error calculating the length of the file\n");
return 1;
}
答案 0 :(得分:3)
始终良好的做法是测试一个函数的返回值并按时处理它,否则可能会发生奇怪的行为,如果不进行详尽的调试,您将无法理解或发现该行为。
您可以在返回值部分中阅读有关fseek
返回值的以下链接:fseek。
此if
语句在代码管道中可忽略不计,而在出现问题时更易于处理。
答案 1 :(得分:2)
您可能需要问自己两个问题:
ftell()
失败了,fseek()
将返回什么?如果fseek()
失败,它将返回非零值。如果ftell()
失败(如果fseek()
失败,则很可能会失败),它将返回-1L
-确定性更好,从错误处理的角度来看,这更好。
然而,fseek()
可能会失败,但不会导致ftell()
失败(可能不太可能,但是失败模式是由实现定义的),因此最好测试{{ 1}},以确保您没有从fseek()
得到错误答案。
由于您的目标是获取文件大小,并且使用ftell()
/ fseek
只是一种综合方法,因此定义文件大小函数更有意义,这样调用方只需要处理无法获得有效文件大小的故障,而不必关注实现细节的故障。关键是,如果您想要文件大小,则不需要处理ftell
的错误,因为这是达到目的的一种手段,并且与您需要实现的目标没有直接关系-{{ 1}}是不确定的副作用,其影响是未知的文件大小-最好表现为“就好像” fseek()
失败,而不会冒实际调用fseek()
的误导行为的风险:
ftell()
然后您的代码将是:
ftell()
然后在应用程序级别,您只需要处理错误long fsize( FILE* file )
{
long size = -1 ' // as-if ftell() had failed
if( fseek( file, 0, SEEK_END ) == 0 )
{
size = ftell( file ) ;
}
return size ;
}
,就对const long file_size = fsize(my_file);
或file_size < 0
失败不感兴趣,只是您不知道文件大小
答案 2 :(得分:1)
fseek
在文件句柄是管道(或串行流)的情况下会返回错误。
此时,ftell
甚至无法告诉您它的位置,因为在这种情况下,“ 无论您走到哪里,都在哪里” 。
答案 3 :(得分:0)
是的,请检查返回值,但在更改类型时要格外小心。
请注意,size_t
的范围可能大于或小于0...LONG_MAX
。
// function returns an error flag
int fsize (FILE * file, size_t *size) {
if (fseek(file, 0, SEEK_END)) {
return 1; // fseek error
}
long ftell_retval = ftell(file);
if (ftell_retval == -1) {
return 1; // ftell error
}
// Test if the file size fits in a `size_t`.
// Improved type conversions here.
// Portably *no* overflow possible.
if (ftell_retval < 0 || (unsigned long) ftell_retval > SIZE_MAX) {
return 1; // range error
}
*size = (size_t) ftell_retval;
return 0;
}
可移植性
考虑到long
的关系,size_t
未定义,将LONG_MAX
直接转换为SIZE_MAX
以及反之亦然是非常困难的。可能是<,==, >
。
请先测试< 0
,然后再测试是否为unsigned long
。 C指定了LONG_MAX <= ULONG_MAX
,所以我们在这里可以。然后将unsigned long
与SIZE_MAX
进行比较。由于这两种类型都是无符号类型,因此比较仅转换为两者中的较大者。再次没有范围损失。