C++17: std::byte und std::filesystem

Inhaltsverzeichnis[Anzeigen]

Mein Artikel C++17: Was gibt's Neues in der Bibliothek war für den ersten Überblick gedacht. Heute geht's mehr in die Details.

Ich warte mit etwas total Neuem beginnen, das ich in meinen vorherigen Artikeln nicht erwähnt habe:

timeline

std::byte

std::byte ist ein Datentyp, der das Konzept eines Bytes umsetzt, wie es im C++ Standard definiert wird. Nun wissen wir was ein Byte ist. Ein Byte ist nicht eine Ganzahl oder ein Buchstabe und daher gegen diese Art von Fehler gefeit. Sein Job ist es auf Speicher zuzugreifen. Konsequenterweise besteht sein Interface nur aus Methoden für bitweise, logische Operationen.

namespace std { 

  template <class IntType> 
    constexpr byte operator<<(byte b, IntType shift); 
  template <class IntType> 
    constexpr byte operator>>(byte b, IntType shift); 
  constexpr byte operator|(byte l, byte r); 
  constexpr byte operator&(byte l, byte r); 
  constexpr byte operator~(byte b); 
  constexpr byte operator^(byte l, byte r); 

} 

 

Du kannst die Funktion std::to_integer(std::byte b) verwenden um einen std::byte in eine Ganzzahl zu konvertieren. Der Aufruf std::byte{integer} konvertiert genau in die andere Richtung. integer muss eine positive Zahl, kleiner als std::numeric_limits<unsigned_char>::max(), sein.

Nun kommt aber etwas, was du schon kennst.

Die Dateisytem Bibliothek

Ich habe bereits in dem Artikel C++17: Was gibt's Neues in der Bibliothek einen ersten Eindruck zur Dateisystem Bibliothek geliefert. Die Bibliothek basiert auf den drei Konzepten einer Datei, eines Dateinamens und eines Pfades. Dateien können Verzeichnisse, harte oder symbolische Links, aber natürlich auch reguläre Dateien sein. Pfade können absolut, kanonisch oder auch relativ sein. Ein kanonischer Pfad enthält keine symbolische Links, die Zeichen "." oder "..",

Du kannst Verzeichnisse erzeugen oder löschen, über sie iterieren oder Eigenschaften von Dateien prüfen.

 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
// filesystem.cpp

#include <fstream>
#include <iostream>
#include <string>
#include <filesystem>
namespace fs = std::filesystem;
 
int main(){

    std::cout << "Current path: " << fs::current_path() << std::endl;

    std::string dir= "sandbox/a/b";
    fs::create_directories(dir);

    std::ofstream("sandbox/file1.txt");
    fs::path symPath= fs::current_path() /=  "sandbox";
    symPath /= "syma";
    fs::create_symlink("a", "symPath");
    
    std::cout << "fs::is_directory(dir): " << fs::is_directory(dir) << std::endl;
    std::cout << "fs::exists(symPath): "  << fs::exists(symPath) << std::endl;
    std::cout << "fs::symlink(symPath): " << fs::is_symlink(symPath) << std::endl;
    

    for(auto& p: fs::recursive_directory_iterator("sandbox"))
        std::cout << p << std::endl;
    // fs::remove_all("sandbox");
    
}

 

Aber im Dateisystem steckt deutlich mehr drin. Daher werde ich jetzt Feature vorstellen, die nicht so offensichtlich sind. Zu mindestens für mich. Ich zeige dir,

  • wie du Dateiberechtigungen verändern,
  • wie du Zeitwerte auslesen und
  • wie du den freien Platz des Dateisystems bestimmen kannst.

Los geht's mit den Dateiberechtigungen.

Berechtigungen

Die Berechtigung wird durch die Klasse std::filesystem::perms repräsentiert. Sie ist ein BitmaskType und kann daher mit bitweisen Operationen modifiziert werden. Die Zugriffsberechtigungen basieren auf  POSIX.

Das Programm von cppreference.com zeigt schön, wie sie die Berechtigungen des Besitzers, der Gruppen und der Welt (other)  biweise ändern lassen.

 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
// perms.cpp

#include <fstream>
#include <bitset>
#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;
 
void printPerms(fs::perms perm){
  std::cout << ((perm & fs::perms::owner_read) != fs::perms::none ? "r" : "-")
            << ((perm & fs::perms::owner_write) != fs::perms::none ? "w" : "-")
            << ((perm & fs::perms::owner_exec) != fs::perms::none ? "x" : "-")
            << ((perm & fs::perms::group_read) != fs::perms::none ? "r" : "-")
            << ((perm & fs::perms::group_write) != fs::perms::none ? "w" : "-")
            << ((perm & fs::perms::group_exec) != fs::perms::none ? "x" : "-")
            << ((perm & fs::perms::others_read) != fs::perms::none ? "r" : "-")
            << ((perm & fs::perms::others_write) != fs::perms::none ? "w" : "-")
            << ((perm & fs::perms::others_exec) != fs::perms::none ? "x" : "-")
            << std::endl;
}


int main(){
  
    std::ofstream("rainer.txt");
 
    std::cout << "Initial file permissions for a file: ";
    printPerms(fs::status("rainer.txt").permissions());
 
    fs::permissions("rainer.txt", fs::perms::add_perms |
                            fs::perms::owner_all | fs::perms::group_all);
    std::cout << "Adding all bits to owner and group:  ";
    printPerms(fs::status("rainer.txt").permissions());
    
    fs::permissions("rainer.txt", fs::perms::remove_perms | 
                           fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write);
    std::cout << "Removing the write bits for all:     ";
    printPerms(fs::status("rainer.txt").permissions());
 
    fs::remove("rainer.txt");
    
}

 

In Zeile 26 habe ich eine neue Datei erzeugt. Dank der globalen Funktion std::filesystem::status::permissions  erhalte ich die Berechtigungen der Datei und kann sie in der Funktion printPerms (Zeile 10-21) darstellen. Nachdem ich die Konstante std::filesystem::add_perms in Zeile 31 gesetzt habe, kann ich alle Berechtigungen für den Besitzer und die Gruppe hinzufügen. Es geht auch andersrum. In Zeile 36 setze ich die Konstante std::filesystem::remove_perms und kann damit die Schreiberechte für alle drei Benutzer entfernen.

Hier ist die Ausgabe des Programms.

perms

Eine Datei besitzt nicht nur das Verständnis von Berechtigung, sondern auch von Zeit.

Zeitwerte

Dank der globalen Funktion std::filesystem::last_write_time kann ich die letzten Schreibzeitpunkt der Datei lesen und setzen. Hier ist ein Beispiel, basierend auf einem Beispiel aus en.cppreference.com.

 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
// fileTime.cpp

#include <iostream>
#include <chrono>
#include <fstream>
#include <filesystem>

namespace fs = std::filesystem;
using namespace std::chrono_literals;

int main(){
    
    fs::path path = fs::current_path() / "rainer.txt";
    std::ofstream(path.c_str()); 
    auto ftime = fs::last_write_time(path);
 
    std::time_t cftime = std::chrono::system_clock::to_time_t(ftime); 
    std::cout << "Write time on server " << std::asctime(std::localtime(&cftime));
    std::cout << "Write time on server " << std::asctime(std::gmtime(&cftime)) << std::endl;
 
    fs::last_write_time(path, ftime + 2h);
    ftime = fs::last_write_time(path); 
 
    cftime = std::chrono::system_clock::to_time_t(ftime);
    std::cout << "Local time on client " << std::asctime(std::localtime(&cftime)) << std::endl;
    
    fs::remove(path);
    
}

 

In Zeile 15 lese ich die Schreibzeit der neu erzeugten Datei aus. Mit Hilfe von ftime (Zeile 17) lässt sich std::chrono::system_clock initialisieren. ftime ist vom Typ std::filesystem::file_time_type. Auf dem Server scheint das ein Alias auf std::chrono::system_clock zu sein. Das ist gut, denn nun kann ich in Zeile 18 std::localtime initialisieren und das Datum darstellen. Falls ich std::gmtime anstelle von std::localtime (Zeile 18) verwende, verändert sich nichts. Das hat mich verwundert, besteht doch in Deutschland eine Differenz von 2 Stunden zwischen Coordinated Universal Time (UTC) und der lokalen Zeit. Das ist aber okay, denn diese Differenz gilt nicht für den Server. UTS und lokale Zeit sind auf dem Server identisch.

Hier ist die Ausgabe des Programms. Zusätzlich siehst du die lokale Zeit in Deutschland. Ich erhielt sie, indem ich 2 Stunden (Zeile 21) zur letzten Schreibzeit der Datei hinzugefügt habe. 

 fileTime

Nun aber zu dem Feature, dass mich am meisten verwundert hat.

Platz Information

Die globale Funktion std::filesystem::space gibt ein std::filesystem::space_info Objekt zurück, das die drei Mitglieder capacity, free, und available besitzt.

  • capacity:Gesamtgröße des Dateisystems
  • free:freier Platz auf dem Dateisystem
  • available:freier Platz für einen nicht-privilegierten Prozess (kleiner oder gleich als free)

Alle Größen sind in Bytes. Die Ausgabe des Programms ist von cppreference.com. All Pfade, die ich ausprobiert habe, befinden sich im gleichen Dateisystem. Daher habe ich immer die gleiche Antwort bekommen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// space.cpp

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main(){
    
    fs::space_info root = fs::space("/");
    fs::space_info usr = fs::space("/usr");
 
    std::cout << ".        Capacity       Free      Available\n"
              << "/    " << root.capacity << "   "
              << root.free << "   " << root.available << "\n"   
              << "usr  "  << usr.capacity << "   "
              << usr.free <<  "   " << usr.available;
              
}

 

Hier sind die Zahlen.

space

 Wie geht's weiter?

Unsere Reise durch die Details von C++17 geht weiter. Im nächste Artikel geht es mit std::string_view weiter.

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Hole dir dein E-Book. Unterstütze meinen Blog.

Tags: C++17

Kommentar schreiben


Abonniere den Newsletter (+ pdf Päckchen)

Beiträge-Archiv

Sourcecode

Neuste Kommentare