1

我有这个惯用的片段来获取二进制文件的长度:

    fseek(my_file, 0, SEEK_END);
    const size_t file_size = ftell(my_file);

…我知道,对于二进制流[ 1 ],迂腐fseek(file, 0, SEEK_END)的行为是未定义的——但坦率地说,在我也没有这个问题的平台上,无论如何这是另一个问题的主题…</p> fstat()

我的问题是:fseek()在这种情况下我应该检查返回值吗?

    if (fseek(my_file, 0, SEEK_END)) {

        return 1;

    }

    const size_t file_size = ftell(my_file);

我从来没有见过fseek()在这样的情况下被检查过,我也想知道fseek()这里可能会返回什么样的错误。

编辑:

在阅读了Clifford 的回答后,我也认为在计算文件大小时处理fseek()和返回值的最佳方法是编写一个专用函数。ftell()但是 Clifford 的好建议不能处理size_t数据类型(毕竟我们需要一个大小!),所以我想最后最实用的方法是使用指针来存储文件的大小,并保留返回我们的专用功能的价值仅适用于失败。这是我对 Clifford 安全尺寸计算器解决方案的贡献:

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;

}
4

4 回答 4

3

测试函数的返回值并按时处理它始终是一个好习惯,否则可能会出现奇怪的行为,如果不进行详尽的调试,您将无法理解或发现这些行为。

您可以在返回值部分阅读以下有关fseek: fseek返回值的链接。

if语句在代码管道中可以忽略不计,但在出现问题时更容易处理。

于 2019-10-21T15:59:10.107 回答
2

你或许需要问自己两个问题:

  1. 如果失败了会ftell()返回什么?fseek()
  2. 我能以任何有意义的方式处理失败吗?

如果fseek()失败,则返回一个非零值。如果ftell()失败(如果失败,它可能会fseek()失败),它将返回-1L- 所以更具确定性,从错误处理的角度来看,这更好。

然而,有fseek()可能失败的潜在方式不会导致ftell()失败(不太可能,但失败模式是实现定义的),因此最好进行测试fseek()以确保您没有从ftell().

由于您的目标是获取文件大小,而使用fseek/ftell只是一种综合方式,因此定义一个文件大小函数更有意义,这样调用者只需关心处理获取文件失败的问题有效的文件大小而不是失败的实现细节。关键是,如果您想要文件大小,则不必处理错误,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没有兴趣,只是你不知道文件大小。fseek()ftell()

于 2019-10-21T17:02:34.343 回答
1

fseek在文件句柄是管道(或串行流)的情况下可以返回错误。
那时,ftell甚至不能告诉你它在哪里,因为在那种情况下,它更像是“无论你走到哪里,你就在那里”

于 2019-10-21T16:09:08.837 回答
1

是的,检查返回值,但要更加小心类型更改。

注意 的范围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;
}

可移植性

鉴于,的关系未定义,直接转换 alongsize_t反之亦然具有挑战性。可能是。LONG_MAXSIZE_MAX<,==, >

相反,首先测试< 0,然后,如果是肯定的,则转换为unsigned long。C 指定LONG_MAX <= ULONG_MAX,所以我们在这里没问题。然后unsigned long比较SIZE_MAX。由于这两种类型都是一些无符号类型,因此比较只是转换为两者中较宽的类型。再次没有范围损失。

于 2019-10-21T21:02:30.093 回答