C++

Std Filesystem C++

A file system keeps and classifies the files on a medium, often one or more storage disks, in a manner that makes them accessible for retrieval. Most modern file systems keep the files organized in a tree-like (or hierarchical) form. One or more root nodes are located at the tree’s top. The filesystem is a library in C++17 standard which enables us to work with the file path, directories, status, and errors of the files in a hierarchical filesystem. The boost.filesystem library serves as the foundation for the creation of this standard library. With this library, we will explore the different functions of the filesystem and their performance in C++17 and access their properties and search. The filesystem header’s std::filesystem namespace offers the filesystem library. We will use the namespace alias given in the following examples in all instances to keep the code simpler.

Example 1: Using Std::Filesystem::Exists

The std::filesystem provides the function which is used to determine whether a file or directory with the specified path or status already exists. For this, we implement a program where we import the library “filesystem” and create a void function “demo”. For the demo function, we give the path and the status function from the class filesystem. Then, we employ the if-else condition to return the statement of the file existence.

Inside the if clause, we invoke the function “fs::exists” and pass the variable “s” for the file status and “pth” for the file path. If the file status exists and the path of the file exists, it prints “Found”. And if the file does not exist, the “Not Found” statement is printed. The main function is implemented with the file path and directory creation.

First, we specify the file path. Then, we create the directory and the symlink for the directory to link the two files. All the functions that we use are supported by the filesystem library. To iterate over the directory entries, we use the directory_iterator function.

#include <iostream>

#include <fstream>

#include <cstdint>

#include <filesystem>

namespace fs = std::filesystem;

 

void demo(const fs::path& pth, fs::file_status s = fs::file_status{})

{

    std::cout << pth;
    if(fs::status_known(s) ? fs::exists(s) : fs::exists(pth))
        std::cout << " Found\n";
    else
        std::cout << "Not Found\n";
}
int main()
{
    const fs::path Filebox{"Filebox"};
    fs::create_directory(Filebox);
    std::ofstream{Filebox/"file"};
    fs::create_symlink("Not Exists", Filebox/"symlink");
     demo (Filebox);
    for (const auto& entry : fs::directory_iterator(Filebox))
        demo (entry, entry.status());
    fs::remove_all(Filebox);

}

For a compilation of C++17 functions and codes, we use the following commands. Hence, we can see the existence of the file path and file status on the output screen:

Example 2: Using Std::Filesystem::File_Size

The std::filesystem::file_size is a function provided by the std::filesystem library of C++. Let’s consider the example where the file path is passed and the associated object is created, along with the file size to be retrieved. As all the functions of the filesystem are supported by the filesystem library, we add the “<filesystem>” library to our program. Then, with the namespace, we label the “std::filesystem” as “fs”.

Then, we utilize the fs::path function and specify the file path “main.c”. Then, we call the std::ofstream and pass the file path object inside it. Also, we use the put() function which takes the character “a” for normal usage. After that, we fetch the file size using the filesystem::file_size function. With the filesystem::remove function, we remove the file path. In the end, we have a try-catch block for catching and throwing the error if it occurs.

#include <iostream>

#include <fstream>

#include <filesystem>

namespace fs = std::filesystem;

int main()

{

    fs::path filepath = fs::current_path() / "main.c";
    std::ofstream(filepath).put('a');
    std::cout << "File size = " << fs::file_size(filepath) << '\n';
    fs::remove(filepath);
 
    try {
        fs::file_size("/dev");
    } catch(fs::filesystem_error& e) {
        std::cout << e.what() << '\n';
    }        

}

We fetched the size of the file by implementing the program with the filesystem::file_size function. We got the file system error of not getting the file size.

Example 3: Using Std::Filesystem::Filesystem_Error Function

When the throwing overloads of the filesystem library functions fail, an error object specified by the class std::filesystem::filesystem error is thrown. We use the system error module that handles the error of the program. We identify the paths of the non-existent files in the subsequent code. The catch block takes the std::filesystem::filesystem_error inside it for throwing the filesystem exception.

Here, we use the different function calls to print the exceptions for files not found. We utilize the std::what() function to detect the exception which returns a null-ended character sequence. Then, we use the path() function with the file_error object “e”. Lastly, we print the value, message, and category generated from the file system error function. We also generate an error code statement from the std::error_code function.

#include <system_error>

#include <filesystem>

#include <iostream>

 

int main()

{

    const std::filesystem::path from{"/Nofile1/a"}, to{"/Nofile2/b"};
    try {
        std::filesystem::copy_file(from, to);
    }
    catch(std::filesystem::filesystem_error const& e) {
        std::cout
            << "what():  " << e.what() << '\n'
            << "path1(): " << e.path1() << '\n'
            << "path2(): " << e.path2() << '\n'
            << "code().value():    " << e.code().value() << '\n'
            << "code().message():  " << e.code().message() << '\n'
            << "code().category(): " << e.code().category().name() << '\n';
    }
 
    std::error_code err;
    std::filesystem::copy_file(from, to, err);
    std::cout << "\nerror_code has non-throwing sets: " << err.message() << '\n';


}

The errors are generated by the file_error function as no such file or directory is found.

Example 4: Using Std::Filesystem::Absolute Function

We have another function whis is the “std::filesystem::absolute” which gives the absolute path of the file. A path is absolute that always includes the base component and the entire directory list that are needed to find the file. Here, inside the main function, we use the std::filesystem::path function and create its object as “fp”. Then, we give the path of the file as “main.c”. We display the current path of the file and the absolute path of the specified file with the cout command. The absolute path is displayed by passing the file path object “fp” in the “std::filesystem::absolute” function.

#include <iostream>

#include <filesystem>

namespace fs = std::filesystem;

int main()

{

    std::filesystem::path fp = "main.c";
    std::cout << "Current path " << fs::current_path() << '\n';
    std::cout << "Absolute path of" << fp << " is "
              << std::filesystem::absolute(fp) << '\n';


}

We can see the current path of the file in our system and the absolute path of the file that is obtained from the filesystem absolute function.

Conclusion

The std::filesystem library, which has a range of functionalities, is introduced in C++17. But we only covered a few functions of the filesystem. A standard library contains highly beneficial and thoughtfully built classes and functions to deal with, and the operating system-specific handling is abstracted away. We implemented the file system function file_exist to know the existence of the file in our first example. Then, we used the file size function to get the file size. Furthermore, we employed the filesystem error function to show how the exceptions are generated in a file. Also, we used the absolute function of the filesystem to fetch the absolute path of the file.

About the author

Omar Farooq

Hello Readers, I am Omar and I have been writing technical articles from last decade. You can check out my writing pieces.