【问题标题】:tellg() function give wrong size of file?tellg() 函数给出错误的文件大小?
【发布时间】:2021-08-04 00:40:29
【问题描述】:

我做了一个示例项目来将文件读入缓冲区。 当我使用 tellg() 函数时,它给我的值比 read 函数实际上是从文件中读取的。我认为有一个错误。

这是我的代码:

编辑:

void read_file (const char* name, int *size , char*& buffer)
{
  ifstream file;

  file.open(name,ios::in|ios::binary);
  *size = 0;
  if (file.is_open())
  {
    // get length of file
    file.seekg(0,std::ios_base::end);
    int length = *size = file.tellg();
    file.seekg(0,std::ios_base::beg);

    // allocate buffer in size of file
    buffer = new char[length];

    // read
    file.read(buffer,length);
    cout << file.gcount() << endl;
   }
   file.close();
}

主要:

void main()
{
  int size = 0;
  char* buffer = NULL;
  read_file("File.txt",&size,buffer);

  for (int i = 0; i < size; i++)
    cout << buffer[i];
  cout << endl; 
}

【问题讨论】:

  • tellg() 返回 -1 吗?您是否尝试过以字符模式打开文件?
  • tellg() 返回一个更大的数字。例如,当我调试时,我看到 i 等于 60,然后 while 循环结束(意味着我们到达了 eof)但tellg 返回 65..
  • ^以文本模式打开文件而不是 ios::binary?
  • ^我不知道。只是想帮忙。顺便说一句,差异一致。增加文件大小,仍然tellg() = file.gcount() + 5??如果是这样,可能 tellg() 也考虑了文件 EOF 字符,而 file.gcount() 没有..

标签: c++ file ifstream


【解决方案1】:

tellg 不报告文件的大小,也不报告偏移量 从头开始,以字节为单位。它报告一个令牌值,它可以 后来习惯找同一个地方,仅此而已。 (甚至不能保证您可以将类型转换为 整数类型。)

至少根据语言规范:在实践中, 在 Unix 系统上,返回的值将是以字节为单位的偏移量 从文件的开头开始,在 Windows 下,它将是 对于在 中打开的文件,从文件开头的偏移量 二进制模式。对于 Windows(和大多数非 Unix 系统),文本 模式,什么之间没有直接和直接的映射 tellg 返回和必须读取的字节数 那个位置。在 Windows 下,您真正​​可以依靠的是 该值将不小于您拥有的字节数 阅读(在大多数实际情况下,不会太大, 虽然最多可以增加两倍)。

如果确切知道可以读取多少字节很重要, 唯一可靠的方法是阅读。你应该 能够通过以下方式做到这一点:

#include <limits>

file.ignore( std::numeric_limits<std::streamsize>::max() );
std::streamsize length = file.gcount();
file.clear();   //  Since ignore will have set eof.
file.seekg( 0, std::ios_base::beg );

最后,关于您的代码的另外两点:

第一行:

*buffer = new char[length];

不应编译:您已将 buffer 声明为 char*, 所以*buffer 具有char 类型,并且不是指针。鉴于什么 您似乎在做,您可能想将buffer 声明为 char**。但更好的解决方案是声明它 作为std::vector&lt;char&gt;&amp;std::string&amp;。 (这样,你 也不必返回大小,并且不会泄漏内存 如果有异常。)

第二,最后的循环条件错误。如果你真的 想一次读一个字符,

while ( file.get( buffer[i] ) ) {
    ++ i;
}

应该可以解决问题。更好的解决方案可能是 读取数据块:

while ( file.read( buffer + i, N ) || file.gcount() != 0 ) {
    i += file.gcount();
}

甚至:

file.read( buffer, size );
size = file.gcount();

编辑:我刚刚注意到第三个错误:如果您无法打开 文件,你不告诉来电者。至少,你应该 将size 设置为 0(但某种更精确的错误 处理可能更好)。

【讨论】:

  • tellg() 返回一个streampos 对象,here 声明«此类的对象支持从 int 构造和转换»,所以至少声明“它甚至不能保证你可以将类型转换为整数类型” 似乎并不真实。
  • @FabioA。 C++14 27.9.1.1,p2: "读写由类 basic_filebuf&lt;charT,traits&gt; 的对象控制的序列的限制与使用标准 C 库文件读写的限制相同。"
  • (cont) C11, 7.21.9.4p2: " 对于文本流,其文件位置指示符包含未指定的信息,可由 fseek 函数用于将流的文件位置指示符返回到其在ftell 调用;两个这样的返回值之间的差异不一定是衡量写入或读取字符数的有意义的指标。"
  • (续)C11 7.21.9.2p3:“二进制流不需要有意义地支持 wherece 值为 SEEK_END 的 fseek 调用。” C11, footnote 268:“将文件位置指示器设置为文件结尾,与 fseek(file, 0, SEEK_END) 一样,对于二进制流具有未定义的行为......”
  • (cont) 所以你不能使用tellg() 来获取文本流的字节数,也不能使用seekg() 来获取二进制流的末尾。我想今天对你来说是个好日子——你学到了一些你以前不知道的东西
【解决方案2】:

在C++17中有std::filesystemfile_size方法和函数,这样可以简化整个任务。

使用这些函数/方法,有机会不打开文件,而是读取缓存数据(尤其是使用 std::filesystem::directory_entry::file_size 方法)

这些函数也只需要目录读取权限而不需要文件读取权限(就像tellg() 一样)

【讨论】:

  • 并不是说这些函数也一定会给出您可以读取的字节数。至少 boost 版本不会——原因很简单,至少在某些系统上,该值是未知的,直到您实际读取字节,这取决于您如何打开文件(文本或二进制文件)。事实是,至少在 Windows 上(当然还有很多其他系统上),如果不实际读取它们,您无法获得可以读取的字节数。
【解决方案3】:
void read_file (int *size, char* name,char* buffer)
*buffer = new char[length];

这些行看起来确实像一个错误:您创建一个 char 数组并保存到 buffer[0] char。然后你读取一个文件到缓冲区,它仍然是未初始化的。

你需要通过指针传递buffer

void read_file (int *size, char* name,char** buffer)
*buffer = new char[length];

或者通过引用,这是c++的方式,更不容易出错:

void read_file (int *size, char* name,char*& buffer)
buffer = new char[length];
...

【讨论】:

  • 是的,但仍然......问题是tellg()返回一个更大的数字
【解决方案4】:
fseek(fptr, 0L, SEEK_END);
filesz = ftell(fptr);

如果通过 fopen 打开文件,将执行该文件

使用 ifstream,

in.seekg(0,ifstream::end);
dilesz = in.tellg();

会做类似的事情

【讨论】:

  • 在什么系统上?它可能会在 Unix 下工作(前提是文件不是太大),但它不会在大多数其他系统上工作。
猜你喜欢
  • 1970-01-01
  • 2013-12-28
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多