Zwei Textdateien miteinander vergleichen



  • 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