Table of Contents

Before C++17 we could often complain that even simple tasks as getting a file size were complicated. With std::filesystem things get a bit easier!

Get a File Size  

STL before C++17 didn’t contain any direct facilities to work with a filesystem. We could only use third party libraries (like Boost), or system APIs.

To get file size, a popular technique was to open a file and then use file position pointer to compute the size.

Here’s some code that uses stream library:

ifstream testFile("test.file", ios::binary);
const auto begin = myfile.tellg();
testFile.seekg (0, ios::end);
const auto end = testFile.tellg();
const auto fsize = (end-begin);

Another option was also to open a file in append mode (std::ios::ate) and then there was no need to move the file pointer - as it was automatically positioned at the end. On Windows you can also use GetFileSizeEx or FindFirstFileEx (as mentioned in a comment by Artem R.):

HANDLE hFile = /* get file/ open/create */

LARGE_INTEGER size;
if (!GetFileSizeEx(hFile, &size))
{
    CloseHandle(hFile);
    return -1; 
}

I haven’t explored all of the possible options, so let ma know what is your way of getting file size.

How about C++17? Is there any chance to have a simpler code and maybe portable?

The Series  

This article is part of my series about C++17 Library Utilities. Here’s the list of the topics in the series:

Resources about C++17 STL:

File Size with std::filesystem  

C++17 brings std::filesystem which streamlines a lot of tasks on files and directories. Not only you can quickly get file size, its attributes, but also create new directories, iterate through files, work with path objects.

The new library gives us two functions that we can use:

  • std::uintmax_t std::filesystem::file_size( const std::filesystem::path& p );
  • std::uintmax_t std::filesystem::directory_entry::file_size() const;

The first function is a free function in std::filesystem, the second one is a method in directory_entry.

Each method also has an overload, as it can throw an exception or return an error code (through an output parameter).

For example, we can get a file size with the following code:

try {
    std::filesystem::file_size("test.file"); 
} catch(fs::filesystem_error& ex) {
    std::cout << ex.what() << '\n';
} 

Or the version with error_codes:

std::error_code ec{};
auto size = std::filesystem::file_size("a.out", ec);
if (ec == std::error_code{})
    std::cout << "size: " << size << '\n';
else
    std::cout << "error when accessing test file, size is: " 
              << size << " message: " << ec.message() << '\n';

You may ask why do we have two methods - as a free function and as a method.

The reason is that directory_entry caches file attributes. That’s why if you iterate over a directory, or you access file several times, then caching might bring performance improvements.

If the file or the directory pointed by directory_entry changes then you need to call the directory_entry::refresh() method to update the cache, otherwise you might get “old” values for your queries.

Demo  

You can play with the code in this interactive sample:

(Here’s also a code at Coliru).

Summary  

In this short post, you’ve seen how to use file_size function from std::filesystem. I encourage you to explore this new and powerful addition to C++17. If you work with files and directories, this might make your code much more comfortable and portable.

See the next article in the series where I discuss File permissions and performance of file_size: std:filesystem::file_size Advantages and Differences