[:tr]Haftalık C++ 6 – Dosya Sistemi Kütüphanesi[:en]Weekly C++ 6 – Filesystem Library[:]

[: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:

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.

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:

İ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:

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:

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:

[: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:

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.

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:

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.

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:

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

[:]