使用C++文件流(fstream),如何确定文件的大小?

99

我相信我在手册中漏掉了这一点,但是如何使用C++ fstream头文件中的istream类确定文件的大小(以字节为单位)?


https://dev59.com/c3E95IYBdhLWcg3whOHK#2289423 - Narendra N
1
@NarendraN - 这个问题明确要求不使用 fstream。 - warren
6个回答

107

你可以使用 ios::ate 标志(和 ios::binary 标志)打开文件,这样 tellg() 函数将直接给你文件大小:

ifstream file( "example.txt", ios::binary | ios::ate);
return file.tellg();

6
在VS 2013 Update5 64位版本中,使用_ios:ate标志而不使用_seekg(0, ios:end)对于大文件可能无效。有关更多信息,请参见http://stackoverflow.com/questions/32057750/how-to-get-the-filesize-for-large-files-in-c。 - x y
2
@displayName cplusplus.com 有点不同意这个说法:它确实使用 tellg() 来检测文件大小。 - Fabio A.
3
以下答案适用于所有情况,但在指针位于文件开头时返回0的情况很多。 - Megha
1
这是完全不正确的答案,在许多情况下会得到0。 - Kyberias

71
你可以寻找到最后,然后计算差值:
std::streampos fileSize( const char* filePath ){

    std::streampos fsize = 0;
    std::ifstream file( filePath, std::ios::binary );

    fsize = file.tellg();
    file.seekg( 0, std::ios::end );
    fsize = file.tellg() - fsize;
    file.close();

    return fsize;
}

15
请问,第一次调用tellg返回0是否有保证? - Steve Jessop
39
我想知道为什么在21世纪我们仍然需要手动计算溪流大小?为什么委员会不只是为此创建一个每个人都可以使用的函数呢?这难道不很简单吗?是否存在任何隐藏的注意事项? - rightaway717
5
他们可以这样做,但为什么要这样做呢?C++不是一种“一切都为你完成”的语言。它提供了构建基础模块的工具,而接下来就由你决定如何使用它们。创建一个只服务于特定用例的函数是完全浪费时间的行为。此外,一个简单的“getter”可能会让人们误解,没有意识到文件搜索所涉及的影响,这可能会在各种情况下对他们产生影响。最好让人们明确地执行此操作。 - Lightness Races in Orbit
23
@LightnessRacesinOrbit: 尽管我理解你的意思,但我仍然不同意。许多其他函数也是基本的,以服务于它们特定的用例,例如获取字符串的长度。这是一个非常常见的操作,为了得到大小并创建一个相应的函数。我相信你在你的代码中也会这样做-为你自己的类提供一个函数来知道它们的大小,如果大小对它们有意义的话。还有很多函数在幕后执行(可能)长时间的操作,例如向量调整大小。这并不意味着我们必须要让人们显式地去做这件事。 - rightaway717
7
在大多数系统中,通过减去tell操作返回的offsets来获取文件大小比直接从inode或类似文件系统结构中存储的文件大小获取要慢。因此,尽管回退算法可以是使用seek和tell的算法,但优化的实现可以绕过该问题并提供更快速的方法。这有什么不好的呢? - Fabio A.
显示剩余14条评论

47

不要使用 tellg 函数确定文件的准确大小。由 tellg 确定的长度将大于可以从文件中读取的字符数。

来自 stackoverflow 问题 tellg() function give wrong size of file?tellg 不报告文件的大小,也不报告以字节为单位的偏移量。它报告一个令牌值,稍后可以用它来寻找相同的位置,没有其他更多信息(甚至不能保证可以将该类型转换为整数类型)。对于 Windows (和大多数非 Unix 系统),在文本模式下,tellg 返回的值与必须读取的字节数之间没有直接的立即映射关系。

如果重要的是要知道可以确切地读取多少字节,则唯一可靠的方法是通过读取来实现。您应该能够使用类似以下内容的代码来实现:

#include <fstream>
#include <limits>

ifstream file;
file.open(name,std::ios::in|std::ios::binary);
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 );

3
我的...我想我还是会继续使用 stat() - Alexis Wilke

11

就像这样:

long begin, end;
ifstream myfile ("example.txt");
begin = myfile.tellg();
myfile.seekg (0, ios::end);
end = myfile.tellg();
myfile.close();
cout << "size: " << (end-begin) << " bytes." << endl;

10
您可以考虑使用更适合的 std::streampos 而非 long,因为后者可能不支持前者那么大的范围。而且,streampos 并不只是一个整数。 - Raphaël Saint-Pierre
你的 begin 不是只有0吗? - ghchoi

6

从C++17开始,我们拥有了std::filesystem::file_size。这并不是严格意义上使用istreamfstream的方法,但它是在标准C++中读取文件大小最简明和正确的方式。

#include <filesystem>
...
auto size = std::filesystem::file_size("example.txt");

1
运行基准测试后,我意识到它至少在Windows上比istream变体运行得更快。 - Octo Poulos
1
它返回实际文件大小还是由操作系统确定的文件大小?由于大多数操作系统使用块来存储空间,因此操作系统将报告比实际大小更大的大小。 - Raildex
1
@Raildex filesize 返回的大小,就像通过 POSIX 获取的结构体的 st_size 成员读取一样确定。摘自 https://en.cppreference.com/w/cpp/filesystem/file_size - Sheyteo

-7

我是一个新手,但这是我自学的方法:

ifstream input_file("example.txt", ios::in | ios::binary)

streambuf* buf_ptr =  input_file.rdbuf(); //pointer to the stream buffer

input.get(); //extract one char from the stream, to activate the buffer
input.unget(); //put the character back to undo the get()

size_t file_size = buf_ptr->in_avail();
//a value of 0 will be returned if the stream was not activated, per line 3.

6
这只是确定是否存在第一个字符。这有什么帮助吗? - warren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接