Zwei Textdateien miteinander vergleichen



  • Hallo,
    ich habe zwei Textdateien, in denen jeweils Hexwerte gespeichert sind.
    Im Normalfall sollten in beiden Dateien die selben Werte gespeichert sein.
    Bei einem anderen Wert soll das Programm einen Fehler ausgeben.
    Habe ein Programm aufgesetzt, aber es tut nicht was es soll.

    #include <cstdlib>
    #include <iostream>
    #include <fstream>
    #include <string.h>
    
    int StringCompare(char *s1, char *s2);
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
        char a;
        char b; 
        int vergleich;
    
        ifstream myfile_1;
        ifstream myfile_2;
    
        myfile_1.open ("example1.txt");
        myfile_2.open ("example2.txt");
    
        while(!myfile_1.eof())
        myfile_1 >> a;
    
        while(!myfile_2.eof())
        myfile_2 >> b;
    
        vergleich = StringCompare(a, b);
    
        if(vergleich == 0)
        cout << "geht\n\n";
        else
        cout << "geht nicht\n\n";
    
        myfile_1.close();
        myfile_2.close();
    
        system("PAUSE");
        return EXIT_SUCCESS;
    }
    
    int StringCompare(*s1, *s2)
    {
    return 0 == strcmp(s1, s2);  
    }
    

    Hab die Lösung mit der StringCompare-Funktion im Internet aufgeschnappt, das ganze funktioniert aber nicht.
    Wer kann mir helfen?

    MfG Kein_Geek 😃


  • Mod

    ⚠ Du sollst nicht blind Code aus dem Internet zusammenkopieren! ⚠

    Das ist eines der fehlerhaftesten Programme die ich je gesehen habe. Selbst das kommentieren der Fehler würde bestimmt 30+ Minuten benötigen. Da kann man eigentlich überhaupt nichts mehr retten, da hilft nur neu schreiben.

    Habe aber gerade keine Zeit, macht bestimmt gleich jemand anders.



  • Jep, bitte sehr:

    #include <iostream>
    #include <fstream>
    
    bool equal(const std::string& filename1, const std::string filename2)
    {
    	std::ifstream f1(filename1.c_str());
    	std::ifstream f2(filename2.c_str());
    	char c1, c2;
    	while(f1.get(c1))
    	{
    		if(!f2.get(c2))
    		{
    			return false;
    		}
    		if(c1 != c2)
    		{
    			return false;
    		}
    	}
    	if(f2.get(c2))
    	{
    		return false;
    	}
    	return true;
    }
    
    int main()
    {
    	if(equal("datei1.txt", "datei2.txt"))
    	{
    		std::cout << "Die Dateien sind gleich." << std::endl;
    	}
    	else
    	{
    		std::cout << "Die Dateien sind unterschiedlich." << std::endl;
    	}
    }
    

    Das Hauptproblem bei deinem Code war, dass du beim Rückgabe Wert "0 == strcmp(s1, s2);" ja schon einen Vergleich hast, diesen aber trotzdem nochmal in der main-Funktion machst. Der Rückgabewert der Vergleichfunktion sollte bool sein.



  • Funktioniert, danke.

    Könntest du die equal-Funktion kurz erklären?

    MfG



  • Sollte eigentlich selbsterklärend sein. Aber hier ne kurze Erklärung:

    Zuerst werden die Dateien geöffnet. Das kann man direkt im Konstruktor machen. Dann benutze ich die get-Funktion (siehe http://www.cppreference.com/wiki/io/basic_istream/get ) um ein Byte zu lesen. Der Rückgabewert von get ist der Stream selbst. Dieser hat eine implizite Umwandlung in bool, die mir sagt, ob es noch Zeichen zu lesen gibt. Die while-Schleife wird also so lange ausgeführt wie es noch Bytes in f1 gibt. Sollte es nicht zu jedem byte ein byte in f2 geben (dafür ist das erste if), bedeutet das, dass f1 größer als f2 ist und damit sind die dateien ungleich -> return false.
    Dann vergleiche ich die Bytes, die müssen schließlich gleich sein. Wenn nicht -> return false.
    Nach der Schleife gucke ich ob f2 noch bytes enthält. Falls ja bedeutet das, dass f2 größer als f1 ist, also ungleich -> return false.
    Ansonsten scheinen die Dateien wirklich gleich zu sein -> return true.



  • Alles klar, danke.



  • Es wäre noch sinnvoll die Größe der Datei zu vergleichen, bevor man diese Byteweise untersucht. Dadurch lässt sich definitiv Zeit sparen.



  • #include <fstream>
    #include <string>
    #include <iostream>
    
    bool equal(const std::string& filename1, const std::string filename2)
    {
        std::ifstream f1(filename1.c_str());
        std::ifstream f2(filename2.c_str());
        f1.seekg(0, std::ios_base::end);
        f2.seekg(0, std::ios_base::end);
        if(f1.tellg() != f2.tellg())
        {
        	return false;
        }
        f1.seekg(0);
        f2.seekg(0);
        char c1, c2;
        while(f1.get(c1) && f2.get(c2))
        {
            if(c1 != c2)
            {
                return false;
            }
        }
        return true;
    }
    
    int main()
    {
        if(equal("datei1.txt", "datei2.txt"))
        {
            std::cout << "Die Dateien sind gleich." << std::endl;
        }
        else
        {
            std::cout << "Die Dateien sind unterschiedlich." << std::endl;
        }
    }
    


  • Wenn du davon ausgehst, dass die Dateien gleich sind und nicht allzu groß, würde ich an deiner Stelle beide Dateien in einen std::string einlesen und vergleichen. Gepuffertes Einlesen ist nämlich AFAIK (deutlich) schneller.

    bool are_files_equal(const char *filename1, const char *filename2)
    {
        std::ifstream i1(filename1), i2(filename2);
        //hier evtl. vorher noch Größenvergleich
        if(!i1.is_open() || !i2.is_open())
        {
            throw std::runtime_error("files could not be opened");
        }
        std::ostringstream s1, s2;
        s1 << i1.rdbuf();
        s2 << i2.rdbuf();
        return s1.get() == s2.get();
    }
    

  • Mod

    Ich will in die gleiche Lücke schlagen wie wxskip, bin nur etwas langsamer beim Schreiben des Beitrags, weil ich auch für den Fall großer Dateien eine funktionierende Funktion wollte:

    #include<cstddef>
    #include<fstream>
    #include<string>
    
    bool fequal(const std::string& filename1, const std::string filename2) 
    {
      std::ifstream f1(filename1.c_str(), std::ios_base::in | std::ios_base::binary);
      std::ifstream f2(filename2.c_str(), std::ios_base::in | std::ios_base::binary);
    
      // Länge herausfinden
      f1.seekg(0, std::ios_base::end);
      f2.seekg(0, std::ios_base::end);
      std::streamsize length1 = f1.tellg();
      std::streamsize length2 = f2.tellg();
    
      // Unterschiedliche Länge -> Dateien verschieden.
      if (length1!=length2) return false;
    
      // Wieder zurück auf Anfang
      f1.seekg(0);
      f2.seekg(0);
      // In Blöcken lesen und vergleichen
      const std::streamsize blocksize=4096;  // Willkürliche Abwägung zwischen Geschwindigkeit und Speicherbedarf
      for (std::streamsize counter=length1; counter > 0; counter-=blocksize)
        {
          char block1[blocksize], block2[blocksize];
          f1.read(block1, blocksize);
          f2.read(block2, blocksize);
          // Ganze ints vergleichen um die bevorzugte Wortgröße des Prozessors auszunutzen
          std::streamsize ints_read=f2.gcount()/sizeof(int);
          for(int i=0; i<ints_read; ++i)
            if (reinterpret_cast<int *>(block1)[i] != reinterpret_cast<int *>(block2)[i])
              return false;
        }
      // Alle Tests erfolgreich, Dateien gleich
      return true;
    }
    

    Jedenfalls habe ich ja neulich gelernt, dass read wesentlich schneller ist als get und ich bin nicht lernresistent.

    P.S.: Ob der Trick mit den ints geschwindigkeitsmäßig etwas bringt weiß ich jetzt nicht, weil ich es nicht ausgiebig getestet habe. Sehr gut möglich, dass dies ohnehin optimiert werden würde.
    P.P.S.: Ach, ich Blindfisch! 😡 Dafür könnte man natürlich auch gleich memcmp nehmen. Das wäre wesentlich besser!



  • @SeppJ

    Hier noch eine kleine Änderung Deines Codes 🙂

    ....
    ....
    // Unterschiedliche Länge -> Dateien verschieden.
      if (length1!=length2) return false;
    
      return ::std::equal(::std::istream_iterator<char>(f1),
                          ::std::istream_iterator<char>(),
                          ::std::istream_iterator<char>(f2));
    }
    

    Schööön kurz 🙂

    Lichtlein


  • Mod

    Da ist aber der gesamte Sinn meines Codes futsch, dass ich eben nicht zeichenweise auslesen möchte.



  • Danke Leute, funktioniert natürlich.
    Wäre es aufwendig wenn man es mit Oberfläche sowie freier Pfad-/Dateiauswahl programmiert?

    MfG



  • Kein_Geek schrieb:

    Wäre es aufwendig wenn man es mit Oberfläche sowie freier Pfad-/Dateiauswahl programmiert?

    Meinst du mit GUI? Kommt auf deine Kenntnisse in diesem Bereich an. Du müsstest dich auch für ein GUI-Framework entscheiden.

    ::std
    

    Wer schreibt denn sowas? 😉



  • SeppJ schrieb:

    P.P.S.: Ach, ich Blindfisch! 😡 Dafür könnte man natürlich auch gleich memcmp nehmen. Das wäre wesentlich besser!

    Und man könnte gleich memory mapped Files verwenden - das wäre dann noch schneller 😉


Anmelden zum Antworten