Größe einer Datei in Bytes finden, für std::fstream, für Verschlüsselung



  • Hallo zusammen,

    ich beschäftige mich etwas mit der einfachen Blockchiffre XTEA und versuche eine einfache ECB Verschlüsselung auf basis der "Referenz" implementierung auf Wikipedia zu machen (also ein kleines Programm, welches beliebige Dateien verschlüsseln kann).

    Dazu meine ich die Dateigröße kennen zu müssen, damit ich weiß, wie oft ich den XTEA anwenden muss (Anzahl der Blöcke). Das läuft also ca. so:

    fstream file{...};
    // ...
    auto fileSize = filesizeX(file);
    auto blockCount = fileSize / blockSize;  // blockSize == 8 bytes
    if (fileSize % blockSize != 0)
        ++blockCount;
    // bin gerade noch dabei über padding zu lesen, damit alles schön funktionieren kann
    
    for (unsigned i = 0; i < blockCount; ++i)
        // Verschlüsseln o.a.
    

    Bisher habe ich folgende 2 Versionen:

    unsigned long filesize1(std::fstream& file)
    {
    	file.ignore(std::numeric_limits<std::streamsize>::max());
    	auto length = file.gcount();
    	file.clear();
    	file.seekg(0, std::ios_base::beg);
    	return static_cast<unsigned long>(length);
    }
    
    unsigned long filesize2(std::fstream& file)
    {
    	file.seekg(0, std::ios::end);
    	auto length = file.tellg();
    	file.seekg(0, std::ios::beg);
    	file.clear();
    	return static_cast<unsigned long>(length);
    }
    
    unsigned long filzesize3(std::fstream& file)
    {
        // ? ... ?
    }
    

    Meine Frage: Welche der beiden Versionen ist die korrekte bzw. bessere, oder gibt es eine noch unbekannte bessere Möglichkeit? Ich kenne mich sehr wenig aus mit den ganzen tellg, seekg etc. Funktionen.

    Ich freue mich über Eure Anregungen.

    LG

    Edit: Nach weiterem Lesen bin ich zu dem Entschluss gekommen, dass man das nicht mit tellg machen sollte (da stimmt wohl in manchen Fällen die Größe nicht). Ob das Andere jetzt besser ist, oder irgendwie ineffizient, weiß ich nicht...

    Die boost Lösung ist etwas umfangreicher...

    boost::uintmax_t file_size(const path& p, error_code* ec)
      {
    #   ifdef BOOST_POSIX_API
    
        struct stat path_stat;
        if (error(::stat(p.c_str(), &path_stat)!= 0 ? BOOST_ERRNO : 0,
            p, ec, "boost::filesystem::file_size"))
          return static_cast<boost::uintmax_t>(-1);
       if (error(!S_ISREG(path_stat.st_mode) ? EPERM : 0,
            p, ec, "boost::filesystem::file_size"))
          return static_cast<boost::uintmax_t>(-1);
    
        return static_cast<boost::uintmax_t>(path_stat.st_size);
    
    #   else  // Windows
    
        // assume uintmax_t is 64-bits on all Windows compilers
    
        WIN32_FILE_ATTRIBUTE_DATA fad;
    
        if (error(::GetFileAttributesExW(p.c_str(), ::GetFileExInfoStandard, &fad)== 0
          ? BOOST_ERRNO : 0, p, ec, "boost::filesystem::file_size"))
              return static_cast<boost::uintmax_t>(-1);
    
        if (error((fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!= 0
          ? ERROR_NOT_SUPPORTED : 0, p, ec, "boost::filesystem::file_size"))
          return static_cast<boost::uintmax_t>(-1);
    
        return (static_cast<boost::uintmax_t>(fad.nFileSizeHigh)
                  << (sizeof(fad.nFileSizeLow)*8)) + fad.nFileSizeLow;
    #   endif
    }
    

  • Mod

    tellg ist die klar überlegene Variante, ignore muss schließlich die ganze Datei unnötig lesen! Der Wert von tellg stimmt, sofern du die Datei auch im Binärmodus öffnest, was du hier aber sicher sowieso haben musst.

    Die Betriebssystemfunktion stat ist nochmals viel besser geeignet. Das schöne ist, stat gibt es auf allen gängigen Systemen, man bringt also keine starke Betriebssystemabhängigkeit herein. Die Boost-Variante scheint dies zu benutzen und dabei auch noch viele mögliche Fehlerquellen korrekt zu behandeln, daher ist die Boost-Variante sicher auch sehr gut.

    Noch besser: Wieso musst du die Zahl im Voraus wissen? Weder in deinem Programm noch in der Referenzimplementierung sehe ich irgendeinen Grund dafür, dass dieser Wert im Voraus bekannt sein müsste. Wäre auch ein ziemlich unpraktischer Algorithmus, wenn das so wäre. Nimm die Daten doch einfach, wie sie kommen.



  • Hey,

    also bezüglich Dateigröße auslesen kenne ich noch das:

    auto length = std::distance( istreambuf_iterator< char >( file ), istreambuf_iterator< char >( ) );
    

    tellg könnte implementierungsabhängig sein und ist somit ggf. nicht portabel (hab ich irgendwo mal gelesen, kann es gerade nicht finden).



  • Also ich würde über sys/stat.h (s. boost Lsg) gehen. Meiner Erfahrung nach, ist es performanter das Dateisystem-Info abzufragen, anstelle in der Datei hin und her zu springen. (Der Lesekopf muss sich ja hin und her bewegen dafür... - außer ihr habt alle eine SSD verbaut. :-))



  • sys/stat.h schrieb:

    Also ich würde über sys/stat.h (s. boost Lsg) gehen. Meiner Erfahrung nach, ist es performanter das Dateisystem-Info abzufragen, anstelle in der Datei hin und her zu springen. (Der Lesekopf muss sich ja hin und her bewegen dafür... - außer ihr habt alle eine SSD verbaut. :-))

    Portabel geht das auch mit std::filesystem.



  • Hallo,

    vielen Dank erstmal für Eure Antworten.

    Nach kurzem Überlegen wird mir jetzt auch klar, dass ich eigentlich garnicht die Dateigröße brauche 💡 Ich habe mich halt an (ich denke) schlechtem (C artigen) Code orientiert, weil ich 0 Ansatz hatte, wie ich den Verschlüsselungsalgorithmus auf Dateien anwenden soll.

    Trotzdem habe ich etwas mitgenommen aus diesem Thread, war also nicht vergebens.

    LG



  • sys/stat.h schrieb:

    anstelle in der Datei hin und her zu springen. (Der Lesekopf muss sich ja hin und her bewegen dafür...

    Nein, muss er nicht, wie kommst du denn darauf?
    Er müsste es, wenn man an der Stelle dann auch wirklich was lesen oder schreiben würde (und wir Caching, Read-Ahead und ähnliches mal vernachlässigen). So lange man bloss den File-Pointer setzt muss der Lesekopf überhaupt nix tun.

    sys/stat.h schrieb:

    - außer ihr habt alle eine SSD verbaut. :-))

    Natürlich haben wir alle ne SSD verbaut, aber das hat damit überhaupt nichts zu tun.


Anmelden zum Antworten