andreas: Alle doppelten Dateien in einem verzeichnis löschen

Hallo!
Ich probiere ein PHP-Script zu schreiben, welches alle doppelten Dateien löscht.
Und zwar handelt es sich um ein Verzeichnis, mit einigen 1000 Dateien, die oft gleich sind(erkennbar an gleicher Größe) aber einen anderen Namen haben(wenn "datei" schon existiert wurde oft "datei2" verwendet...., aber leider nach keinem vernünftigen Muster)
Ich dachte da an eine Schleife, in der ich mit filesize() überprüfe ob eine Datei genau so groß ist, wie die aktuelle, und ggfs mit unlink() lösche. Aber ich hab das ein paar grundsätzlicher Probleme.
Wie kann ich innerhalb der Schleife wieder die Dateien weiter durchsuchen, eine Schleife in der Schleife ist doch nicht wirklich gut, oder?

<?php
$handle=opendir ('.');
while (false !== ($file = readdir ($handle))) {
$size1=filesize($file);

$handle2=opendir ('.');
while (false !== ($file2 = readdir ($handle2))) {
$size2=filesize($file2);

if ($size1==$size2){
unlink($file2);}
}

}
closedir($handle);
?>

Wie macht man sowas richtig?

Grüße
  Andreas

  1. hi!

    Wie kann ich innerhalb der Schleife wieder die Dateien weiter
    durchsuchen, eine Schleife in der Schleife ist doch nicht wirklich
    gut, oder?

    Ich wüsste nicht, was gegen eine Schleife innerhalb einer Schleife
    spricht. Allerdings ist es sehr ungünstig, jedesmal wieder die Größe
    aller Dateien abzufragen.

    Mach es besser so, dass du nur einmal am Anfang den Dateinamen und
    die Größe jeder Datei einliest und irgendwo in einer geeigneten
    Datenstruktur zwischenspeicherst. Dann kannst du alleine im Speicher
    die ganzen Einträge durchgehen und miteinander vergleichen, was
    bedeutend schneller geht, als ständige Plattenzugriffe.

    Wenn du dabei zwei Dateien mit gleicher Größe erwischst, kannst du
    diese zusammen an einer anderen Stelle zwischenspeichern und sofort
    bzw. irgendwann später miteinander vergleichen, um eventuelle eine
    davon zu löschen.

    bye, Frank!

    1. Hi!
      Hast recht, das wird das beste sein. Ich kann ja einfach eben alles(Name/Größe) in eine Tabelle schreiben, und dann nach doppelten Einträgen suchen, das wurde ja schon öfter hier diskutiert. Dann habe ich eine nette Liste, die ich evtl noch mit unlink() abarbeiten kann. Frag mich zwar ob ich das nicht lieber von Hand mache, aber so hab ich wenigstesn was dabei gelernt:)
      Auch wenns am Ende wahrscheinlich länger dauern wird!(könnte schon bald bei der Hälfte angelangt sein:)
      Jedenfalls vielen Dank!
      Grüße
        Andreas

      PS: Eien frage noch: Sollte ich lieber dir() oder lieber readdir() verwenden?Unterschiede?

      1. Moin!

        PS: Eien frage noch: Sollte ich lieber dir() oder lieber readdir() verwenden?Unterschiede?

        Der Unterschied der beiden besteht darin, dass ich das eine kenne und von dem anderen noch nie was gehoert hab. ;-) Also was zum Teufel ist dir() ?

        So long

        --
        Alle Verallgemeinerungen sind falsch.

        1. hi!

          PS: Eien frage noch: Sollte ich lieber dir() oder lieber readdir
          () verwenden? Unterschiede?
          Der Unterschied der beiden besteht darin, dass ich das eine kenne
          und von dem anderen noch nie was gehoert hab. ;-) Also was zum
          Teufel ist dir() ?

          Vielleicht meint er http://www.php.net/manual/en/class.dir.php?

          Anscheinend verwendet das aber auch readdir(), also ist es wohl im
          Endeffekt ziemlich egal.

          Alle Verallgemeinerungen sind falsch.

          Der Babier rasiert genau die, die sich nicht selber rasieren... ;)

          bye, Frank!

          1. Moin!

            Vielleicht meint er http://www.php.net/manual/en/class.dir.php?

            Oh oops, irgendwie bin ich in Gedanken zu Perl ruebergeschwenkt, wo es kein dir gibt. *g*

            Alle Verallgemeinerungen sind falsch.
            Der Babier rasiert genau die, die sich nicht selber rasieren... ;)

            Naja, der Konflikt beim oberen loest sich auf, wenn man annimmt, dass die Aussage falsch ist. Das Barbierproblem ist in der Formulierung, wie Du sie hier bringst, tatsaechlich nicht loesbar. Allerdings wird es auch oft als "Der Barbier rasiert alle Männer, die sich nicht selber rasieren" ausgedrueckt, und dann kann man es imho in einer Weise auffassen, wo sich kein Konflikt ergibt.

            So long

            --
            Alle Verallgemeinerungen sind falsch.

            1. hi!

              Vielleicht meint er http://www.php.net/manual/en/class.dir.php?
              Oh oops, irgendwie bin ich in Gedanken zu Perl ruebergeschwenkt,
              wo es kein dir gibt. *g*

              Wie gut, dass daran nicht die Uhrzeit schuld ist... *g*

              Alle Verallgemeinerungen sind falsch.
              Der Babier rasiert genau die, die sich nicht selber rasieren...
              Naja, der Konflikt beim oberen loest sich auf, wenn man annimmt,
              dass die Aussage falsch ist.

              Das war mir bewusst :)

              Das Barbierproblem ist in der Formulierung, wie Du sie hier
              bringst, tatsaechlich nicht loesbar. Allerdings wird es auch oft
              als "Der Barbier rasiert alle Männer, die sich nicht selber
              rasieren" ausgedrueckt, und dann kann man es imho in einer Weise
              auffassen, wo sich kein Konflikt ergibt.

              Wenn man es so auffassen kann, dass es konfliktfrei lösbar ist, dann
              ist es ja langweilig. Wer hat denn neulich gefordert, dass ich meine
              Aussagen klarer und eindeutiger formulieren soll? ;)

              bye, Frank!

              1. Hi Frank,

                Wer hat denn neulich gefordert, dass ich meine
                Aussagen klarer und eindeutiger formulieren soll? ;)

                keine Ahnung, aber er klingt spontan sympathisch. <eg>

                Viele Grüße
                      Michael

  2. Moin,

    Ich probiere ein PHP-Script zu schreiben, welches alle doppelten Dateien löscht.

    Au fein, eine Programmiertechnikfrage.
    Also ich denke eine der effizientesten Sachen wäre etwas in der Art von

    Gehe alle Dateien durch
    | Überlege dir für die aktuelle Datei irgendein eindeutiges Erkennungsmerkmal (md5(implode("",file($dateiname))) wäre so eins)
    | schau in einem assoziativen Array nach, ob du das Erkennungsmerkmal schon mal gesehen hast (if($deinArray[$erkennungsmerkmal]))
    | +-falls ja: lösche die Datei
    | +-falls nein: merke dir das Erkennungsmerkmal in dem Array ($deinArray[$erkennungsmerkmal] = true;)

    Nachdem die Schleife durchgelaufen ist, hast du von jeder Datei nur eine Kopie (die Benennung dieser Kopie ist nicht deterministisch).

    PS: Statt eines assoziativen Arrays kannst du jede andere Struktur nehmen die einfache Überprüfungen, darauf ob ein Objekt in ihr drin ist, zulässt (binäre Bäume irgendwer?). Ich gehe aber mal davon aus dass die PHP-Leute das bei den Arrays schon ganz gut gemacht haben. Statt des implode()-Einzeilers zum Dateiauslesen solltest du vielleicht besser den fopen(); fread(,filesize()); fclose();-Dreizeiler nehmen, der müsste um einiges performanter sein.

    --
    Henryk Plötz
    Grüße von der Ostsee

    1. hi!

      Au fein, eine Programmiertechnikfrage.

      Warum änderst du dann nicht die Kategorie? ;)

      Also ich denke eine der effizientesten Sachen wäre etwas in der
      Art von [...]
      Nachdem die Schleife durchgelaufen ist, hast du von jeder Datei
      nur eine Kopie

      Jepp. War ja eigentlich ungefähr das, was ich vorgeschlagen habe.
      Nur etwas klarer ausformuliert... ;)

      (die Benennung dieser Kopie ist nicht deterministisch).

      Was heißt das im Klartext?

      PS: Statt eines assoziativen Arrays kannst du jede andere Struktur
      nehmen die einfache Überprüfungen, darauf ob ein Objekt in ihr
      drin ist, zulässt (binäre Bäume irgendwer?).

      Wenn die PHP-Programmierer Ahnung von ihrem Job haben, dann ist
      anzunehmen, dass sie die assoziativen Arrays bereits mit vernünftigen
      Hashmaps implementiert haben, die theoretisch schneller sein dürften
      (meist konstante Zugriffszeit) als eine selbst-implementierte Version
      mit binären Bäumen (mindestens logarithmische Zugriffszeit).

      bye, Frank!

      ps. So spät nachts kann ich vielleicht nicht mehr klar denken. Für
      eventuelle Fehler übernehme ich daher keine Garantie... ;)

      1. Moin,

        Au fein, eine Programmiertechnikfrage.
        Warum änderst du dann nicht die Kategorie? ;)

        Och, ich hatte offline geschrieben und zum Kategorieändern muss man online sein, da hatte ich dann doch kein Lust mehr drauf.

        (die Benennung dieser Kopie ist nicht deterministisch).
        Was heißt das im Klartext?

        Ich wollte nur noch mal "The filenames are not returned in any particular order." (aus der readdir()-Doku) erwähnt haben. Dieser Algorithmus behält die Kopie übrig die readdir() als erstes ausspuckt, und das ist eher selten etwa die die in der alphabetischen Sortierung (oder irgendeiner anderen Sortierung) vorne steht. Wenn du also "Datei", "Kopie von Datei" und "Kopie von Kopie von Datei"(verdammt, ich hab die letzten Tage zuviel Zeit vor einem Windows-Rechner verbracht ;) hast, dann bleibt heute vielleicht Kopie von Datei, morgen Datei und übermorgen die kopierte Kopie übrig.

        Andreas: Wenn du auch noch eine alphabetisch oder sonstwie sortierte Version übrigbehalten willst, musst du erst alle Dateinamen in ein Array einlesen, dieses dann mit einem Sortieralgorithmus deiner Wahl bewerfen (http://www.php.net/manual/de/function.natcasesort.php zum Beispiel) und dann die Elemente dieses Arrays mit dem Algorithmus durchgehen.

        --
        Henryk Plötz
        Grüße von der Ostsee

        1. Moin,

          Au fein, eine Programmiertechnikfrage.
          Warum änderst du dann nicht die Kategorie? ;)

          ja, aber auch [PHP] :)

          Was mich noch interessieren würde, wann verwendet man fopen()... und wann opendir(), readdir()...?
          Ich verstehe irgendwie nicht ganz den Unterschied?! Dachte immer mit fopen() öffnet man die Datei, aber warum sollte man das? Es reichen doch Name und Größe zu wisssen  und entsprechend zu löschen oder zu kopieren oder wie auch immer! fopen() sollte dagegen doch recht langsam sein, wenn erst alle Dateien geöffnet werden, oder denke ich hier irgendwie falsch?

          Grüße
            Andreas

          1. Hoi !

            Was mich noch interessieren würde, wann verwendet man fopen()... und wann opendir(), readdir()...?
            Ich verstehe irgendwie nicht ganz den Unterschied?! Dachte immer mit fopen() öffnet man die Datei, aber warum sollte man das? Es reichen doch Name und Größe zu wisssen  und entsprechend zu löschen oder zu kopieren oder wie auch immer! fopen() sollte dagegen doch recht langsam sein, wenn erst alle Dateien geöffnet werden, oder denke ich hier irgendwie falsch?

            Es kann Dir durchaus passieren, daß Du zwei oder noch mehr Dateien mit identischer Größe aber total unterschiedlichem Inhalt hast. Wenn Du nur die Größen vergleichst, dann löscht Du mit Sicherheit eine der Dateien (trotzdem sie von der übriggebliebenen verschieden ist).

            Henryk benutzt deshalb für seine Lösung den MD5-Wert (eine Art Quersumme) der Datei: Damit kannst Du ziemlich sicher (99,9999999999999999999(usw.)%) alle doppelten Dateien entfernen, und jeweils ein Exemplar behalten, egal, ob sie die gleiche Größe haben oder nicht.

            Dafür ist die Lösung von Henryk in der Tat _wesentlich_ langsamer.

            Ciao,

            Harry