[:tr]Herkese merhabalar, bir aradan sonra (umarım çok da uzun olmayan), yeni bir haftalık C++ yazımız ile birlikteyiz. Bu yazımda, sizler ile birlikte uzun bir süre beklenen (aslında Java ve .Net gibi bir çok platform ile sunulan ama C++ da standart bir şekilde sunulmayan) STL kabiliyeti olan, dosya sistemi kütüphanesine bir göz atacağız.
Bu kütüphane, aslında uzunca bir süredir (yaklaşık 2003) kullanılmakta olan boost::filesystem kütüphanesini baz alıyor diyebiliriz. O kütüphane de oldukça yaygın ve birçok kişi tarafından kullanılıyor, ayrıca merak edenler için de 😉 boost ve standart dosya sistemi kütüphanelerini karşılaştıran bir bağlantı veriyorum. Elbette standart dosya sistemi kütüphanesinin en önemli amacı sizin de tahmin edebileceğiniz üzere bir çok platform için standart kod yazabilmek. Bu yazımda da, kütüphanenin öne çıkan özellikleri üzerinden geçip, bunları örnek kullanımlarla sizlere aktarmaya çalışacağım. Bunun ile birlikte daha detaylı bilgi edinmek için de sizlere ilgili kaynakları sunacağım. Bu arada eğer fırsat bulabilirsem, hazırlayacağım bazı haftalık C++ yazılarında bu kütüphanenin örnek kullanımlarını verebilirim.
Giriş:
Stackoverflow yazısında ifade edildiği üzere, bu kütüphaneye yapılan değişikliklere ilişkin bir çok yayın mevcut. Bunlara aşağıdaki bağlantılardan ulaşabilirsiniz:
- P0219R1, göreceli yol desteği eklenmesi,
- P0317R1, directory_entry’ye ön bellek (cache) eklenmesi,
- P0492R2, gelen görüşler ışığında yapılan bir çok hata ve güncellemeler,
- P0430R2, bazı Non-POSIX sistemler için destek,
- P0218R1, nihai yayın.
Kütüphaneye dair güncellemeleri “LWG issue list” listesinden takip edebilirsiniz. Ayrıca C++ standardının diğer parçalarına/kabiliyetlere dair hususları da buradan takip edebilirsiniz.
Ayrıca C++ standart dokümanı içerisindeki 30.10 başlığından da dosya sistemine dair detaylara ulaşabilirsiniz. Fakat dokümanın, oldukça büyük olduğunu ifade etmeliyim 🙂
Yukarıda da bahsettiğim üzere, kütüphane boost::filesystem’e dayanıyor ve kütüphane tarafından sunulan bazı bileşenler de opsiyonel. Bir diğer deyişle her platform ya da derleyici ile kullanılmayabilir (ör. sembolik kısayollar FAT-32 de desteklenmemektedir). Bunların yanında, aşağıda bazı platform (işletim sistemi) ile ilintili hususları da göz önüne almanızda fayda var:
- Dosya karakter duyarlılığı. Ör. “file.txt”, “File.txt” ve “filet.TXT” her ne kadar Windows tabanlı işletim sistemlerinde aynı dosyaya karşılık gelse de Linux gibi POSIX tabanlı işletim sistemlerinde ise üç farklı dosyaya karşılık gelmektedir,
- Göreceli/tam dosya yolu yaklaşımları. POSIX benzeri sistemlerde “/bin” dizini tam dosya dizinine karşılık gelse de, Windows tabanlı işletim sistemlerinde böyle bir yaklaşım hiç bulunmamaktadır,
Kütüphanedeki en önemli kavram dosya/dizin yolu (“path”). Bu aslında herhangi bir dosyanın (aynı zamanda dizinin) konumunu ifade eden etiket olarak nitelendirebiliriz. Tabi bu sadece dosya değil, aynı zamanda dizin, kısa yol, katı bağlantı ya da sembolik bağlantıları da ifade edebilir. Yol kavramının bu anlamda ifade edebileceği bütün değerleri görmek için dosya sistemi kütüphanesi sayfasına bakabilirsiniz. Bu yollar göreceli ya da bütün yolu ifade edebilir. Bazı platformların jenerik ya da kendilerine özel yol formatları da olabiliyor. Ör, Windows üzerinde “/tmp/test.txt” geçerli bir format olarak kabul ediliyor, “\tmp\test.txt” yolu da destekleniyor. Bu ikisi aynı yolu ifade eden platforma özgü gösterimler. Bu tarz gösterimler, platformlardan platforma değişebilmekte. Kütüphane bu tarz platform bağımlı dosya yolu ile daha jenerik gösterimler arasında dönüşüm için de API sunmakta.
Bu doğrultuda bakacak olursak genel yol formatı aşağıdaki gibi özetlenebilir:
The generic path format can be summarized as:
[rootname] [rootdir] [relativepath]
where:
- Burada opsiyonel olan kök ismi (“rootname”), platforma özgüdür (ör. POSIX sistemlerde “//host”, Windows tabanlı sistemlerde “C:” olabilmektedir.),
- Bir diğer opsiyonel öğe kök dizini de dizin ayıracı olarak kullanılıyor,
- Son olarak göreceli yol ise dosya ve dizin isimlerini içeren kısımdır.
Sizin de tahmin edebileceğiniz üzere dosya sistemi kütüphanesi oldukça büyük bir kütüphane ve bütün detaylarını bir yazıda vermek oldukça zor. Burada olabildiğince önemli olan kabiliyetlerin üzerinde durup, daha çok pratik kullanım alanı olan özelliklerini örnek kodlar ile vermeye çalışacağım. Daha fazla detay öğrenmek isterseniz, sizlere kaynaklar kısmındaki Nicolai. M. Jouttis ve diğer C++ 17 kitaplarını önerebilirim.
Dosya sistemi kütüphanesini kullanmak için <filesystem> başlık dosyasını eklemeli ve filesystem:: alanını kullanmalısınız. Kütüphane içerisinde hem sınıflar hem de bağımsız metot şeklinde API’ler bulunmakta. Varsayılan olarak, kütüphane hataları istisna mekanizması (filesystem_error istisna nesnesi) ile kotarıyor (diğer kütüphanelere göre doğası gereği bu tarz durumların çok olabileceğini tahmin edersiniz diye düşünüyorum). İstisna mekanizması yanında isterseniz, ilgili hata kodlarını da parametre olarak dönen API’leri kullanabilirsiniz (std::error_code).
İlgili alan uzayı ve başlık dosyası derleyici ve IDE’nize göre değişiklik gösterebilir, bu konuya son başlıkta (derleyici desteği) değineceğim.
Bu kadar açıklama yeter sanırım, hemen bir örnek ile kütüphaneyi kullanmaya başlayalım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <fstream> #include <iostream> #include <string> #include <experimental/filesystem> namespace fs = std::experimental::filesystem; int main() { /// current_path metodu uygulamanın çalışdığı dizin std::cout << "Current path: " << fs::current_path() << std::endl; /// "sandbox/a/b" dizinlerini olustur std::string dir = "sandbox/a/b"; fs::create_directories(dir); std::ofstream("sandbox/file1.txt"); // Sembolik bağlantılar Windows'ta olmadığı için aşağıdaki gibi koruma altına alıyoruz. #ifndef WIN32 fs::path symPath = fs::current_path() /= "sandbox"; symPath /= "syma"; fs::create_symlink("a", "symPath"); std::cout << "fs::symlink(symPath): " << fs::is_symlink(symPath) << std::endl; std::cout << "fs::exists(symPath): " << fs::exists(symPath) << std::endl; #endif std::cout << "fs::is_directory(dir): " << fs::is_directory(dir) << std::endl; // Dizin içeriğini özyineli bir şekilde gösterelim for (auto& p : fs::recursive_directory_iterator("sandbox")) std::cout << p << std::endl; // kök dizin ve altındaki bütün her şeyi silelim fs::remove_all("sandbox"); return 0; } |
Yukarıda görebileceğiniz üzere dizin işlemlerini sunulan bu API’ler ile kolay bir şekilde gerçekleştirebilirsiniz. Peki daha başka neler yapabiliriz:
- Dizin oluşturma/içeriğini izleme,
- Dizin düzenlemeleri ve karşılaştırma,
- Dizin isimlerinin platform özgü ve jenerik dönüşümleri
- Dizin ve dosyalar arasında dolaşma,
- Dosya niteliklerini, durumunu ve yetkileri izleme/değiştirme,
- Dosya işlemleri (oluşturma, silme, kopyalama, taşıma),
- Çok daha fazlası için https://en.cppreference.com/w/cpp/filesystem sayfasına bir göz atabilirsiniz
Yol Nesneleri:
Şimdi en çok kullanılan nesnelerden birisi olan yol nesnelerine göz atalım. Aslında bakarsanız aşağıdaki örnek bu sınıfa ilişkin çoğu örnek kullanımı göstermekte. Bunların bir çoğu günlük kullanımda ihtiyaç duyabileceğiniz kabiliyetler. Hemen bakalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | #include <fstream> #include <iostream> #include <string> #include <time.h> #include <filesystem> using namespace std; namespace fs = std::experimental::filesystem; // İlgili yolu yukarıda bahsettiğim şekilde parça parça göstermek için kullanılan metot. // Görebileceğiniz üzere aslında yol nesnesinin kendisi de bir konteyner void iteratePathContent(const fs::path& initialPath) { int i = 0; for (const auto& part : initialPath) cout << "Path part: " << i++ << " = " << part << '\n'; } // Verilen dosyanın boyutunu hesaplayan metot // Hata durumunda -1 döner uintmax_t calculateFileSize(const fs::path& filePath) { // uintmax_t => en geniş işaretsiz tam sayı uintmax_t totalSize = static_cast<uintmax_t>(-1); if (fs::exists(filePath) && fs::is_regular_file(filePath)) { auto err = error_code{}; auto filesize = fs::file_size(filePath, err); if (filesize != static_cast<uintmax_t>(-1)) { totalSize = filesize; } } return totalSize; } // Verilen dizinin boyutunu hesaplayan metot // Hata durumunda -1 döner uintmax_t calculateDirectorySize(const fs::path& pathToCheck) { auto size = static_cast<uintmax_t>(-1); if (fs::exists(pathToCheck) && fs::is_directory(pathToCheck)) { for (auto const & entry : fs::recursive_directory_iterator(pathToCheck)) { if (fs::is_regular_file(entry.status()) || fs::is_symlink(entry.status())) { auto err = error_code{}; auto filesize = fs::file_size(entry, err); if (filesize != static_cast<uintmax_t>(-1) && err != error_code()) size += filesize; } } } return size; } // Verilen yol nesnesine ilişkin bilgileri sunan metot void displayPathInfo(const fs::path& pathToShow) { int i = 0; cout << "Displaying path info for: " << pathToShow << '\n'; for (const auto& part : pathToShow) { cout << "path part: " << i++ << " = " << part << '\n'; } try { cout << "exists() = " << fs::exists(pathToShow) << '\n' << "root_name() = " << pathToShow.root_name() << '\n' << "root_path() = " << pathToShow.root_path() << '\n' << "relative_path() = " << pathToShow.relative_path() << '\n' << "parent_path() = " << pathToShow.parent_path() << '\n' << "filename() = " << pathToShow.filename() << '\n' << "stem() = " << pathToShow.stem() << '\n' << "extension() = " << pathToShow.extension() << '\n'; cout << "canonical() = " << fs::canonical(pathToShow) << '\n'; } catch (fs::filesystem_error err) { cout << "exception: " << err.what() << '\n'; } } // Dosya ismi, yolu, boyutu ve en son düzenlenme tarihini gösteren metot void displayFileInfo(const fs::directory_entry & entry, string &lead, fs::path &filename) { time_t cftime = chrono::system_clock::to_time_t(fs::last_write_time(entry)); // _s siz asctime API sini de kullanabilirsiniz char timeStr[26]; asctime_s(timeStr, sizeof timeStr, localtime(&cftime)); cout << lead << " " << filename << ", " << calculateFileSize(filename.c_str()) << ", time: " << timeStr; } // Verilen yol içeriğinin özyineli bir şekilde gösterilmesi void traverseAndDisplayFolderTree(const fs::path& pathToShow, int level) { if (fs::exists(pathToShow) && fs::is_directory(pathToShow)) { auto lead = string(level * 3, ' '); for (const auto& entry : fs::directory_iterator(pathToShow)) { auto filename = entry.path().filename(); if (fs::is_directory(entry.status())) { cout << lead << "[+] " << filename << '\n'; traverseAndDisplayFolderTree(entry, level + 1); cout << '\n'; } else if (fs::is_regular_file(entry.status())) displayFileInfo(entry, lead, filename); else cout << lead << " [?]" << filename << '\n'; } } } // Yukarıdaki dizinlerin özyineli bir şekilde gösterilmesinin ilklendirilmesinde kullanılan metot void traverseAndDisplayFolderTree(const fs::path& initialPath) { traverseAndDisplayFolderTree(initialPath, 0); } int main(int argc, char* argv[]) { // yol nesnesi ile /= and += operatörlerini kullanabilirsiniz. İlki yol formatına uygun bir şekilde ilgili metni ekler, ikincisi ise direk ekler fs::path examplePath("C:\\"); examplePath /= "Windows"; examplePath /= "System"; cout << examplePath << '\n'; examplePath += "32"; cout << examplePath << '\n'; // Verilen yola ilişkin bilgileri döner displayPathInfo("C:\\windows\\system.ini"); // Verilen yol içeriğini özyineli bir şekilde göster iteratePathContent("C:\\windows\\system.ini"); // Verilen dizin içeriğini öz yineli bir şekilde göster const fs::path pathToShow{ argc >= 2 ? argv[1] : fs::current_path() }; traverseAndDisplayFolderTree(pathToShow); return 0; } |
Boş Disk Alanı Bilgisi:
Bir diğer kullanışlı sınıf ise std::filesystem::space_info. Bu sınıf sayesinde dosya sistemine ilişkin boş ve kullanılabilir alan bilgileri aşağıda gösterildiği şekilde sorgulanabilir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <iostream> #include <filesystem> using namespace std; namespace fs = std::experimental::filesystem; int main() { fs::space_info devi = fs::space("/windows/system32"); fs::space_info tmpi = fs::space("C:\\"); cout << ". Capacity Free Available\n" << "/windows/system32: " << devi.capacity << " " << devi.free << " " << devi.available << '\n' << "/tmp: " << tmpi.capacity << " " << tmpi.free << " " << tmpi.available << '\n'; } |
İzinler:
Aşağıda gösterildiği şekilde dosyaların izin nitelikleri ile de oynayabilirsiniz. Bu anlamda BitmaskTypetipinde olan std::filesystem::perms maskeleri kullanılmakta (detaylar için https://en.cppreference.com/w/cpp/filesystem/perms)’a göz atabilirsiniz). Aşağıda ilgili izinleri nasıl görüntüleyip, değiştirebileceğinizi bulabilirsiniz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <fstream> #include <bitset> #include <iostream> #include <filesystem> namespace fs = std::experimental::filesystem; using namespace std; void displayPermissions(fs::perms p) { cout << ((p & fs::perms::owner_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::owner_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::owner_exec) != fs::perms::none ? "x" : "-") << ((p & fs::perms::group_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::group_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::group_exec) != fs::perms::none ? "x" : "-") << ((p & fs::perms::others_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::others_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::others_exec) != fs::perms::none ? "x" : "-") << '\n'; } int main() { // Dosya oluştur ofstream("test.txt"); cout << "Created file with permissions: "; displayPermissions(fs::status("test.txt").permissions()); // İzinleri değiştir fs::permissions("test.txt", fs::perms::owner_all | fs::perms::group_all | fs::perms::add_perms); cout << "After adding o+rwx and g+rwx: "; displayPermissions(fs::status("test.txt").permissions()); // Dosyayı sil fs::remove("test.txt"); } |
Son Düzenleme Zamanı:
Bu kütüphane ve std::chrono kütüphanesini de kullanarak herhangi bir dosyanın son düzenlenme tarihini görebilirsiniz. Aşağıdaki örnek kod, bu kullanımı göstermekte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <iostream> #include <chrono> #include <fstream> #include <filesystem> namespace fs = std::experimental::filesystem; using namespace std::chrono_literals; using namespace std; int main() { // Bir yol tanımlayalım fs::path path = fs::current_path() / "example.txt"; // Dosya oluştur ofstream exampleFile(path.c_str()); exampleFile.close(); // Son yazma zamanını oku auto ftime = fs::last_write_time(path); time_t cftime = chrono::system_clock::to_time_t(ftime); cout << "File write time is " << asctime(localtime(&cftime)) << '\n'; // Dosya zamanını 3 saat ileri alalım fs::last_write_time(path, ftime + 3h); // Dosya zamanını tekrar okuyalım ftime = fs::last_write_time(path); cftime = chrono::system_clock::to_time_t(ftime); // TR zamanı cout << "File write time is " << asctime(localtime(&cftime)) << endl; // Dosyayı silelim fs::remove(path); return 0; } |
Derleyici/Kütüphane Desteği:
Kullandığınız derleyicinin bu kabiliyeti destekleyip desteklemediğini, diğer kabiliyetler ile birlikte https://en.cppreference.com/w/cpp/compiler_support adresinden kontrol edebilirsiniz.
Derleyicinizin sürümüne göre std::experimental::filesystem isim alanı kullanmanız gerekebilir.
GCC: “-lstdc++fs” opsyionu eklenmelidir. Ayrıca “<experimental/filesystem>” başlığını eklemeniz gerekiyor.
Clang: Clang 5.0 ile destek sunuluyor. https://libcxx.llvm.org/cxx1z_status.html
Visual Studio: Visual studio 2017 için ise <filesystem> başlığını ekleyip, std::experimental::filesystem isim alanını kullanmalısınız (yukarıdaki örnekler de bu şekilde).
Kaynaklar:
- http://www.modernescpp.com/index.php/c-17-more-details-about-the-library
- https://en.cppreference.com/w/cpp/experimental/fs
- http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf
- http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf
- https://stackoverflow.com/questions/48347642/differences-between-c14-stdexperimentalfilesystemv1-and-c17-stdfiles
- https://www.packtpub.com/application-development/c17-stl-cookbook
- https://www.amazon.com/Modern-Programming-Cookbook-multithreading-networking/dp/1786465183
- http://www.cppstd17.com/
- https://stackoverflow.com/questions/40899267/how-similar-are-boost-filesystem-and-the-standard-c-filesystem-libraries
- https://www.boost.org/doc/libs/1_68_0/libs/filesystem/doc/index.htm
- https://en.cppreference.com/w/cpp/compiler_support
[:en]Hello everybody, after a break (hopefully not very long), we are together again with another weekly C++ post. In this post, we are going to explore a very anticipated standard library feature which is filesystem library (Well Java and .Net have this feature for a very long time why not us).
This library is based on boost::filesystem which is available for a long time (since 2003) so if you would like to use that path well you can (I also added a link for comparison of boost and std version comparison for curious;). The most obvious benefit of this library as you may guess is to write multiplatform and standard code. In this post, we will go over most obvious features of library, and illustrate the possible usage via examples and provide references for more details. I may provide some example usage of this library in following posts.
Overview:
As pointed out in Stackoverflow Post, there are some papers making changes to current filesystem library which are listed as follow:
- P0219R1, adding relative paths support,
- P0317R1, adding caching to directory_entry,
- P0492R2, a lengthy list of fixes and changes in response to national body comments,
- P0430R2, support for certain non-POSIX systems,
- P0218R1, final paper.
In fact, the latest status can be observed via in the LWG issue list. You can check out other capabilities’ status.
You can also check out the complete standard in which filesystems are explained under section 30.10 (input/output library). Beware that, this document is little bit large:)
Well as I mentioned above, library is based on boost::filesystem and some of its components are optional which means that you may not use every feature on every platform or compiler (i.e. symbolic links are not supported in FAT-32). You also need to take some platform specific issues into consideration such as:
- File name case sensivity. For instance, “file.txt” and “File.txt” and “file.TXT” might refer to the same file in Windows OS or three different files POSIX-based OS like Linux,
- Absolute/relative path approach. On some systems “/bin” is an absolute path in POSIX-based OS es, while on others it is not Windows OS.
The most important concepts of this library is path. It is a label which possibly represents a location of a file within system which may not be of course a file. It may represent folders, hard links, symbolic links or regular files (standard files). You can check the web site of filesystem library for all enumerations. Paths on the other hand can be absolute or relative. Some platforms may have generic/native path formats. On Windows OS, the generic format “/tmp/test.txt” is a valid native format besides “\tmp\test.txt“, which is also supported (thus, “/tmp/test.txt” and “\tmp\test.txt” are two native versions of the same path). Of course, library provides APIs to convert paths between the native and generic format.
The generic path format can be summarized as:
[rootname] [rootdir] [relativepath]
where:
- The optional root name is implementation specific (e.g., it can be “//host” on POSIX systems and “C:” on Windows systems),
- The optional root directory is a directory separator,
- The relative path is a sequence of file names separated by directory separators.
Filesystem library is a very large library which is not in fact possible to cover each detail in this post so I will try to focus on most important and practical features through code examples. If you want to learn more details, I highly recommend Nicolai M. Josuttis and other C++ 17 books that I give in references which cover filesystems in a more detailed fashion.
The filesystem library is provided through <filesystem> and filesystem namespace. There are both classes and freestanding functions in library. By default, library handles errors (which may occur more than other libraries because of its nature) through exceptions (filesystem_error exception), however you can also get error code (std::error_code) if you pass an additional parameter to corresponding API (in addition to return value).
The namespace and include header may change according to you compiler, please check last section and you compiler support page for details.
Let’s start to look at file system library with a simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <fstream> #include <iostream> #include <string> #include <experimental/filesystem> namespace fs = std::experimental::filesystem; int main() { /// current_path return the current directory as you may guess std::cout << "Current path: " << fs::current_path() << std::endl; /// Create "sandbox/a/b" folders std::string dir = "sandbox/a/b"; fs::create_directories(dir); std::ofstream("sandbox/file1.txt"); // sym links not available for windows. exception is thrown #ifndef WIN32 fs::path symPath = fs::current_path() /= "sandbox"; symPath /= "syma"; fs::create_symlink("a", "symPath"); std::cout << "fs::symlink(symPath): " << fs::is_symlink(symPath) << std::endl; std::cout << "fs::exists(symPath): " << fs::exists(symPath) << std::endl; #endif std::cout << "fs::is_directory(dir): " << fs::is_directory(dir) << std::endl; // print directory content recursively for (auto& p : fs::recursive_directory_iterator("sandbox")) std::cout << p << std::endl; // remove root and all sub content fs::remove_all("sandbox"); return 0; } |
Here, you can see that basic folder operations can easily be performed by these APIs. So what can we do else with this library? Well briefly:
- Create/inspect paths,
- Path modifications and comparisons,
- Path name conversion between native and generic formats,
- Iterating over directories/files,
- Working with file attributes/flags, status and permissions,
- File system modifications (create/delete/copy/move files),
- For much more, check out https://en.cppreference.com/w/cpp/filesystem.
Path Objects:
Let us start with path object which probably used most. Following code example summarize mosst common usage of path object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | #include <fstream> #include <iostream> #include <string> #include <time.h> #include <filesystem> using namespace std; namespace fs = std::experimental::filesystem; // Print the path according to path format that I mentioned above where you can use each part // path is a container at the same time void iteratePathContent(const fs::path& initialPath) { int i = 0; for (const auto& part : initialPath) cout << "Path part: " << i++ << " = " << part << '\n'; } // Method to calculate the size of given file // Returns -1 for error uintmax_t calculateFileSize(const fs::path& filePath) { // uintmax_t => maximum widht unsigned integer uintmax_t totalSize = static_cast<uintmax_t>(-1); if (fs::exists(filePath) && fs::is_regular_file(filePath)) { auto err = error_code{}; auto filesize = fs::file_size(filePath, err); if (filesize != static_cast<uintmax_t>(-1)) { totalSize = filesize; } } return totalSize; } // Method to calculate size the of given directory // Returns -1 for error uintmax_t calculateDirectorySize(const fs::path& pathToCheck) { auto size = static_cast<uintmax_t>(-1); if (fs::exists(pathToCheck) && fs::is_directory(pathToCheck)) { for (auto const & entry : fs::recursive_directory_iterator(pathToCheck)) { if (fs::is_regular_file(entry.status()) || fs::is_symlink(entry.status())) { auto err = error_code{}; auto filesize = fs::file_size(entry, err); if (filesize != static_cast<uintmax_t>(-1) && err != error_code()) size += filesize; } } } return size; } // Display given information about path void displayPathInfo(const fs::path& pathToShow) { int i = 0; cout << "Displaying path info for: " << pathToShow << '\n'; for (const auto& part : pathToShow) { cout << "path part: " << i++ << " = " << part << '\n'; } // Catch any exception that may caused because of above function calls try { cout << "exists() = " << fs::exists(pathToShow) << '\n' << "root_name() = " << pathToShow.root_name() << '\n' << "root_path() = " << pathToShow.root_path() << '\n' << "relative_path() = " << pathToShow.relative_path() << '\n' << "parent_path() = " << pathToShow.parent_path() << '\n' << "filename() = " << pathToShow.filename() << '\n' << "stem() = " << pathToShow.stem() << '\n' << "extension() = " << pathToShow.extension() << '\n'; cout << "canonical() = " << fs::canonical(pathToShow) << '\n'; } catch (fs::filesystem_error err) { cout << "exception: " << err.what() << '\n'; } } // Display file name, path, size and last modified time void displayFileInfo(const fs::directory_entry & entry, string &lead, fs::path &filename) { time_t cftime = chrono::system_clock::to_time_t(fs::last_write_time(entry)); // You can also use non _s version char timeStr[26]; asctime_s(timeStr, sizeof timeStr, localtime(&cftime)); cout << lead << " " << filename << ", " << calculateFileSize(filename.c_str()) << ", time: " << timeStr; } // Display the content of path recursively using above methods void traverseAndDisplayFolderTree(const fs::path& pathToShow, int level) { if (fs::exists(pathToShow) && fs::is_directory(pathToShow)) { auto lead = string(level * 3, ' '); for (const auto& entry : fs::directory_iterator(pathToShow)) { auto filename = entry.path().filename(); if (fs::is_directory(entry.status())) { cout << lead << "[+] " << filename << '\n'; traverseAndDisplayFolderTree(entry, level + 1); cout << '\n'; } else if (fs::is_regular_file(entry.status())) displayFileInfo(entry, lead, filename); else cout << lead << " [?]" << filename << '\n'; } } } // A recursive funtion to initiate a traver from given path void traverseAndDisplayFolderTree(const fs::path& initialPath) { traverseAndDisplayFolderTree(initialPath, 0); } int main(int argc, char* argv[]) { // you can se /= and += operators. the former is used path concatenation and latter is used just for appending string fs::path examplePath("C:\\"); examplePath /= "Windows"; examplePath /= "System"; cout << examplePath << '\n'; examplePath += "32"; cout << examplePath << '\n'; // current_path return the current directory as you may guess displayPathInfo("C:\\windows\\system.ini"); // iterate over path content iteratePathContent("C:\\windows\\system.ini"); // traverse and display folders and content const fs::path pathToShow{ argc >= 2 ? argv[1] : fs::current_path() }; traverseAndDisplayFolderTree(pathToShow); return 0; } |
Disk Space Info:
Another useful object is std::filesystem::space_info which can be used to obtain total size of the filesystem, free space and available space on the filesystem as follow:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <iostream> #include <filesystem> using namespace std; namespace fs = std::experimental::filesystem; int main() { fs::space_info devi = fs::space("/windows/system32"); fs::space_info tmpi = fs::space("C:\\"); cout << ". Capacity Free Available\n" << "/windows/system32: " << devi.capacity << " " << devi.free << " " << devi.available << '\n' << "/tmp: " << tmpi.capacity << " " << tmpi.free << " " << tmpi.available << '\n'; } |
Permissions:
You can also access and play with permission attributes of files which is illustrated in following example. “std::filesystem::perms” which is BitmaskType is used with some predefined bit masks which can be found at (https://en.cppreference.com/w/cpp/filesystem/perms). Follwing sample code shows how you can read and update these permissions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <fstream> #include <bitset> #include <iostream> #include <filesystem> namespace fs = std::experimental::filesystem; using namespace std; void displayPermissions(fs::perms p) { cout << ((p & fs::perms::owner_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::owner_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::owner_exec) != fs::perms::none ? "x" : "-") << ((p & fs::perms::group_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::group_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::group_exec) != fs::perms::none ? "x" : "-") << ((p & fs::perms::others_read) != fs::perms::none ? "r" : "-") << ((p & fs::perms::others_write) != fs::perms::none ? "w" : "-") << ((p & fs::perms::others_exec) != fs::perms::none ? "x" : "-") << '\n'; } int main() { // create file ofstream("test.txt"); cout << "Created file with permissions: "; displayPermissions(fs::status("test.txt").permissions()); // change permissions fs::permissions("test.txt", fs::perms::owner_all | fs::perms::group_all | fs::perms::add_perms); cout << "After adding o+rwx and g+rwx: "; displayPermissions(fs::status("test.txt").permissions()); // Remove file fs::remove("test.txt"); } |
Last Modified Time:
You can also use filesystem library with other libraries such as std::chrono to display last modified time of given file. An example usage is illustrated below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <iostream> #include <chrono> #include <fstream> #include <filesystem> namespace fs = std::experimental::filesystem; using namespace std::chrono_literals; using namespace std; int main() { // You can define a path as follow fs::path path = fs::current_path() / "example.txt"; // Create a file ofstream exampleFile(path.c_str()); exampleFile.close(); // Obtain last write time auto ftime = fs::last_write_time(path); time_t cftime = chrono::system_clock::to_time_t(ftime); cout << "File write time is " << asctime(localtime(&cftime)) << '\n'; // move file write time 3 hour to the future fs::last_write_time(path, ftime + 3h); // read back from the filesystem ftime = fs::last_write_time(path); cftime = chrono::system_clock::to_time_t(ftime); // TR time cout << "File write time is " << asctime(localtime(&cftime)) << endl; // Remove file fs::remove(path); return 0; } |
Compiler/Library support:
By the way, the compiler support of this feature can be checked with other C++ features via https://en.cppreference.com/w/cpp/compiler_support.
Depending on the version of your compiler you might need to use std::experimental::filesystem namespace.
GCC: “-lstdc++fs” should be specified when you want filesystem. “<experimental/filesystem>“ header should be included.
Clang should be ready with Clang 5.0
https://libcxx.llvm.org/cxx1z_status.html
Visual Studio: In case of Visual Studio, you need to include <filesystem> and std::experimental::filesystem namespace should be used.
References
- http://www.modernescpp.com/index.php/c-17-more-details-about-the-library
- https://en.cppreference.com/w/cpp/experimental/fs
- http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4100.pdf
- http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf
- https://stackoverflow.com/questions/48347642/differences-between-c14-stdexperimentalfilesystemv1-and-c17-stdfiles
- https://www.packtpub.com/application-development/c17-stl-cookbook
- https://www.amazon.com/Modern-Programming-Cookbook-multithreading-networking/dp/1786465183
- http://www.cppstd17.com/
- https://stackoverflow.com/questions/40899267/how-similar-are-boost-filesystem-and-the-standard-c-filesystem-libraries
- https://www.boost.org/doc/libs/1_68_0/libs/filesystem/doc/index.htm
- https://en.cppreference.com/w/cpp/compiler_support
[:]
