zip magic number auslesen



  • Guten Abend,

    ich bin neu hier im Forum. Ich bin dabei mir privat C++ anzueignen und hänge gerade an einer Stelle. Vielleicht könnt ihr mir weiterhelfen.
    Und zwar versuche ich gerade die magic number eines zip files, welche sich ja am Anfang der Datei befinden sollte, auszulesen.

    Das gelingt mir zum Teil auch, wenn ich mir den binären Inhalt in eine .txt Datei schreiben lasse.
    Wenn ich mir den Inhalt der zip Datei jedoch direkt über cout ausgeben lasse, habe ich ähnlichen Buchstabensalat, aber die ersten beiden Zeichen sind nicht PK.

    Ich benutze MS Visual Studio 2019 Community.

    	void zipwrite(int a, char* buffer)
    	{
    		std::ofstream file;
    		file.open("C:/test/file/zipcontent.txt", std::ios::ate | std::ios::in);
    		if (file.is_open())
    		{
    			for (int i = 0; i < a; i++)
    			{
    				file << buffer[i]; // Geschrieben in zipcontent.txt wird (erste Zeile): PK    éT9Q_§üN   W   	   testfile.txtŒ±
    			}
    			file << "\n\n";
    			file.close();
    		}
    		else
    		{
    			std::cerr << "Could not open file!";
    		}
    
    	}
    
    	void bin()
    	{
    		char* buffer;						
    		std::ifstream datei;
    		datei.open("C:/test/file/testfile.zip", std::ios::binary | std::ios::ate | std::ios::in);
    		if (datei.is_open())
    		{
    			int dateigroesse = datei.tellg();
    			buffer = new char[dateigroesse];
    			datei.seekg(0, std::ios::beg);
    			datei.read(buffer, dateigroesse);
    			datei.close();
    			std::cout << "Inhalt der Datei: " << std::endl;
    			for (int i = 0; i < dateigroesse; i++)
    			{
    				std::cout << buffer[i]; // Über cout ausgegeben wird (erste Zeile): Ç0♀♦{ñý`▬`º³NW  testfile.txt§î▒
    			}
    
    			zz::zipwrite(dateigroesse, buffer);
    
    			delete[] buffer;
    			buffer = nullptr;	
    		}
    		else
    		{
    			std::cerr << "Datei konnte nicht geöffnet werden.";
    		}
    	}
    

    Ich frage mich jetzt, wieso das so ist, weil ich ja ein und den selben dynamischen Speicher 'buffer' verwende?!

    Gruß direkt



  • Das könnte nur so aussehen, da es Steuerzeichen gibt, mit denen sich bereits ausgegebene Zeichen wieder überschreiben lassen. Schau dir mal die Ausgabe dieses Programms an:

    #include <iostream>
    
    auto main() -> int
    {
        std::cout << char{ 'P' } << char{ 'K' } << char{ 13 } << char{ 'X' } << char{ 'X' } << std::endl;
    }
    

    Unter der Windows-Konsole gibt dieses (scheinbar) lediglich XX aus. Das liegt daran, dass wenn der ASCII-Code 13 (Carriage Return) dazu führt, dass der "Ausgabe-Cursor" wieder auf die erste Zeichenposition in der Zeile springt und im folgenden bereits ausgegebene Zeichen überschrieben werden.

    Möglicherweise ist das hier dein Problem. Bei mit cout ausgegebenen Binärdateien ist das sogar sehr wahrscheinlich.



  • @direkt Zeig mal ein vollständiges minimales Programm daß das Verhalten zeigt. Bitte kein unnötiger quatsch sondern ein minimales Programm das das Verhalten reproduziert. Sonst ist das alles nie passiert.



  • Hallo,

    vielen Dank für die Rückmeldungen.

    @Finnegan
    Tatsächlich. Das ist sehr einleuchtend - vielen Dank.
    Ist dir ein Weg bekannt, der diese ungünstige Problematik umgeht?

    @Swordfish
    Hallo Swordfish,
    bei dem Code handelt es sich bereits um das Wesentliche.
    Mehr als zz:bin(); mache ich in main() auch nicht, da es sich hierbei nur um eine Übung handelt.

    #include <iostream>
    #include <fstream>
    #include <string>
    
    namespace zz
    {
    	void zipwrite(int a, char* buffer)
    	{
    		std::ofstream file;
    		file.open("C:/test/file/zipcontent.txt", std::ios::ate | std::ios::in);
    		if (file.is_open())
    		{
    			for (int i = 0; i < a; i++)
    			{
    				file << buffer[i];
    			}
    			file << "\n\n";
    			file.close();
    		}
    		else
    		{
    			std::cerr << "Could not open file!";
    		}
    
    	}
    
    	void bin()
    	{
    		char* buffer;	
    		std::ifstream datei;
    		datei.open("C:/test/file/testfile.zip", std::ios::binary | std::ios::ate | std::ios::in);
    		if (datei.is_open())
    		{
    			int dateigroesse = datei.tellg();	
    			buffer = new char[dateigroesse];	
    			datei.seekg(0, std::ios::beg);		
    			datei.read(buffer, dateigroesse);	
    			datei.close();	
    			std::cout << "Inhalt der Datei: " << std::endl;
    
    			for (int i = 0; i < dateigroesse; i++)
    			{
    				std::cout << buffer[i];
    			}
    
    			zz::zipwrite(dateigroesse, buffer);
    
    			delete[] buffer;
    			buffer = nullptr;
    		}
    		else
    		{
    			std::cerr << "Datei konnte nicht geöffnet werden.";
    		}
    	}
    	
    
    }
    
    int main()
    {
    	zz::bin();
    	return 0;
    }
    


  • @direkt sagte in zip magic number auslesen:

      	for (int i = 0; i < dateigroesse; i++)
      	{
      		std::cout << buffer[i];
      	}
    

    Ersetze dein cout mal durch folgendes:

    if (buffer[i] < 32) 
        std::cout << "(Sonderzeichen " << static_cast<int>(buffer[i]) << ")";
    else
        std::cout << buffer[i];
    

    Bei deinem ofstream in zipwrite: Warum nutzt du da ios::in und ios::ate?
    Ich würde erwarten:
    Statt

    std::ofstream file;
    file.open("C:/test/file/zipcontent.txt", std::ios::ate | std::ios::in);
    

    Dies hier:

    std::ofstream file("C:/test/file/zipcontent.txt", std::ios::binary);
    

    Es gibt keinen Grund, das Erstellen des ofstream-Objektes und das Öffnen zu trennen.
    Und wenn du einen Buffer schreiben willst, dann mach das bitte nicht zeichenweise, sondern mit write, analog zum read.

    Analog bei dem ifstream. Ifstream sind input streams, also schon automatisch mit ios::in. Du kannst also auch

    std::ifstream datei;
    datei.open("C:/test/file/testfile.zip", std::ios::binary | std::ios::ate | std::ios::in);
    

    ersetzen durch

    std::ifstream datei("C:/test/file/testfile.zip", std::ios::binary | std::ios::ate);
    


  • Und wenn wir schon dabei sind... new/delete sollte man in modernem C++ nur noch seltenst sehen. Den lokalen Puffer kannst du besser mit einem std::vector<char> umsetzen:

    void dump_to_cout( std::vector<char> const& vec )
    {
       std::cout << "Inhalt der Datei: " << std::endl;
       for( char const ch : vec )
       {
          // nur druckbareZeichen ausgeben, für alle anderen einen Punkt (beliebig anpassbar)
          if( std::isprint( ch ) ) std::cout << ch;
          else std::cout << '.';
       }
    }
    
    void bin()
    {
       std::ifstream datei("C:/test/file/testfile.zip", std::ios::binary | std::ios::ate | std::ios::in);
       if (datei.is_open())
       {
          int dateigroesse = datei.tellg();	
          if( dateigroesse > 0 )
          {
             std::vector<char> buffer( dateigroesse );
             datei.seekg(0, std::ios::beg);		
    	 datei.read(buffer.data(),buffer.size());	
    	 datei.close();	
    
             dump_to_cout( buffer );
             zz::zipwrite( buffer.size(), buffer.data() );
          }
          else
          {
             std::cerr << "Datei hat keinen Inhalt\n";
          }
       }
       else
       {
          std::cerr << "Datei konnte nicht geöffnet werden.";
       }
    }
    

    Wobei bin auch ein äußerst bescheidener Funktionsname ist, da kann man nur erahnen, dass da iwas mit Binärdaten passiert.



  • @wob

    Ersetze dein cout mal durch folgendes:

    if (buffer[i] < 32) 
        std::cout << "(Sonderzeichen " << static_cast<int>(buffer[i]) << ")";
    else
        std::cout << buffer[i];
    

    Ich sehe den Effekt, verstehe aber nicht, warum dort jetzt das PK zu sehen ist? Vor allem: vor dem ersten "(Sonderzeichen)". & Warum 32?
    Ich denke mir fehlt noch etwas Hintergrundwissen um deine Botschaft zu verstehen.

    Bei deinem ofstream in zipwrite: Warum nutzt du da ios::in und ios::ate?

    Weil ich in dem Fall wollte, dass ich mehrmals den Inhalt in die selbe Datei schreiben kann.

    Analog bei dem ifstream. Ifstream sind input streams, also schon automatisch mit ios::in.

    Habe ich angepasst - sieht auch eleganter aus, danke 😉
    Das mit dem effizienten Code hab ich noch nicht so raus.

    @DocShoe

    Und wenn wir schon dabei sind... new/delete sollte man in modernem C++ nur noch seltenst sehen. Den lokalen Puffer kannst du besser mit einem std::vector<char> umsetzen ...

    Okay, gut zu wissen.
    Ich werde mir deinen Quellcode speichern und später beim Thema Vektoren nacharbeiten, danke 😉



  • @direkt sagte in zip magic number auslesen:

    Ich sehe den Effekt, verstehe aber nicht, warum dort jetzt das PK zu sehen ist? Vor allem: vor dem ersten "(Sonderzeichen)". & Warum 32?

    Das PK ist jetzt zu sehen weil es vorher wahrscheinlich durch irgendwelche Steuerzeichen überschrieben worden ist und jetzt nicht mehr. Nicht druckbares (<32, besser die std::is...() Funktionen nehmen aus <cctype>, schau Dir eine beliebige ASCII-Tabelle an) wird jetzt als Zahl ausgegeben und bewirkt kein Backspace oder andere lustige Dinge mehr.

    Minimal ist sowas:

    #include <fstream>
    #include <iostream>
    #include <vector>
    
    int main()
    {
        std::ifstream is{ 'foo.zip', std::ios_base::binary };
    
        is.seekg(0, std::ios_base::end);
        std::vector<char> data{ is.tellg() };
        is.seekg(0, std::ios_base::beg);  // danke Belli
        
        is.read(&data[0], filesize);
    
        for (auto ch : data)
            std::cout << ch;
        std::cout.put('\n');
    }
    

    aber nicht Deine Romane.



  • @Swordfish sagte in zip magic number auslesen:

    is.seekg(0, std::ios_base::end);

    Zeile 11 sollte wohl:

    is.seekg(0, std::ios_base::beg);
    

    heißen.



  • @Swordfish

    "Deine Romane"

    Da ich erst seit 2 Wochen dabei bin, mag meine Art zu programmieren sehr umständlich und primitiv sein 😉
    Ich denke, wenn ich die Syntax erstmal richtig verinnerlicht habe, wird mein Code auch etwas kompakter.

    @Belli @DocShoe @Finnegan @Swordfish @wob

    Die Inhalte hier werde ich mir nochmal zu Gemüte führen und in meine Aufzeichnungen integrieren.
    Vor allem das Thema Vektoren sollte mich hier ein Stück weiterbringen.

    Ich bedanke ich mich bei allen für euer Feedback 🙂


Anmelden zum Antworten