Dr. Ma-Busen: Kleines Problem beim Kopieren von Dateien mit ein CGI Script

Hallo!

Ich bin dabei mir ein CGI Script zu schreiben. Ich habe da eine Sub drin die eine Datei kopiert. Das ganze funktioniert auch, nur das Problem ist wenn ich  Binärdateien kopiere. Manche sind nachher unbrauchbar, kaputt.
Kann mir einer sage wo das Problem liegt?

Hier ist mal die Sub die, die Dateien kopiert.

sub kopieren() {
 my $quelle = shift;
 my $ziel = shift;
 my $status = 0;
 unless (-d $quelle) {
  open(Quelle,"$quelle");
  open(Ziel,">$ziel");
  binmode(Ziel) if (-B Quelle);
  while ($status == 0) {
   if (eof(Quelle)) {
    $status = 1;
    last;
   }
   print Ziel getc(Quelle);
  }

close(Ziel);
  close(Quelle);
 }
}

Bitte verweist mich jetzt nicht auf irgendwelche Module. Ich weis das es Module für so etwas gibt, aber ich möchte das ganze ohne Module machen.

MfG
Dr. Ma-Busen

  1. Hallo!

    Ich bin dabei mir ein CGI Script zu schreiben. Ich habe da eine Sub drin die eine Datei kopiert. Das ganze funktioniert auch, nur das Problem ist wenn ich  Binärdateien kopiere. Manche sind nachher unbrauchbar, kaputt.
    Kann mir einer sage wo das Problem liegt?

    Hier ist mal die Sub die, die Dateien kopiert.

    use File::Copy;

    Bitte verweist mich jetzt nicht auf irgendwelche Module. Ich weis das es Module für so etwas gibt, aber ich möchte das ganze ohne Module machen.

    Das ist keine kluge Einstellung. Es gibt für solche Kleinigkeiten geteste und oft Geschwindigkeitsoptimierte (einige sind in C geschrieben) Module.
    Alles selber machen, macht kein Mensch, es sei denn, du hast etwas extrem Zeitkritisches, dann sollte man aber auch kein Perl verwenden.

    Struppi.

    1. Hallo!

      Danke für die schnelle Antwort.

      Das ist keine kluge Einstellung. Es gibt für solche Kleinigkeiten geteste und oft Geschwindigkeitsoptimierte (einige sind in C geschrieben) Module.
      Alles selber machen, macht kein Mensch, es sei denn, du hast etwas extrem Zeitkritisches, dann sollte man aber auch kein Perl verwenden.

      Ich weis das es Module dafür gibt und das die auch alle getestet und optimiert sind. Aber wenn ich Module verwende dann weis ich nacher zwar wie man Module einbaut und was die können. Aber dadurch lerne ich nichts. Wenn ich das alles selber mache dann lerne ich aber, wie man das macht und wie man so etwas dann Optimiert.

      MfG
      Dr.Ma-Busen

      1. Ich weis das es Module dafür gibt und das die auch alle getestet und optimiert sind. Aber wenn ich Module verwende dann weis ich nacher zwar wie man Module einbaut und was die können. Aber dadurch lerne ich nichts. Wenn ich das alles selber mache dann lerne ich aber, wie man das macht und wie man so etwas dann Optimiert.

        Wenn dein Prgramm nur dazu da ist eine Datei zu kopieren, dann hast du recht, aber selbst dann ist ein 2-Zeiler angenehmer.

        Du lernst übrigens den Umgang mit Modulen, was nicht zu unterschätzen ist, da in diesen oft langjährige Programmiererfahurng steckt, die dir helfen kann, Wege zu zeigen, wie du dein Probleme löst.

        Guck dir doch mal File::Copy an, wie die das machen.

        Struppi.

        1. Guck dir doch mal File::Copy an, wie die das machen.

          Hab sie mir angeschaut. Und festgestellt das ich nur für die Ziel Datei den Binärmodus verwendet habe jetzt verwende ich den Binärmodus für beide und es funzt. *juhu*

          Das Programm ist übrings ein teil aus ein File Manager den ich mir gerade schreibe.

          MfG
          Dr. Ma-Busen

  2. Moin Moin !

    binmode(Ziel) if (-B Quelle);

    Ziel und Quelle sind sehr unglücklich gewählte Namen für Filehandles, es ist Konvention, Filehandles komplett groß zu schreiben: ZIEL und QUELLE.

    Aus der Doku: The -T and -B switches work as follows. The first block or so of the file is examined for odd characters such as strange control codes or characters with the high bit set. If too many strange characters (>30%) are found, it's a -B file, otherwise it's a -T file. Also, any file containing null in the first block is considered a binary file. If -T or -B is used on a filehandle, the current stdio buffer is examined rather than the first block. Both -T and -B return true on a null file, or a file at EOF when testing a filehandle. Because you have to read a file to do the -T test, on most occasions you want to use a -f against the file first, as in next unless -f $file && -T $file.

    Oder kurz: Perl *rät*, ob QUELLE binär ist oder nicht. Und es kann *falsch* *raten*. Genau das passiert offensichtlich bei Dir. Wenn Du eine Datei kopieren willst, willst Du sie *immer* binär kopieren, so lange sie auf dem System bleibt. Also schlicht "binmode QUELLE; binmode ZIEL;".

    Zum Rest deines Codes:

    sub kopieren()
                ^^-- *so* hat die Funktion keine Argumente ...
    {
     my $quelle = shift;
     my $ziel = shift;
       ^-- aber Du versuchst, gleich zwei zu bekommen. Das kann nur zufällig funktionieren.

    "sub kopieren($$)" ist richtig, "sub kopieren" ganz ohne Klammern geht auch, dann ist aber nicht garantiert, daß Du überhaupt Argumente bekommst.

    Benutze das w-Flag und das strict-Pragma, dann passieren solche Fehler nicht mehr:

    #!/usr/bin/perl -w
    use strict;

    Du prüfst nicht, ob open() fehlschlägt. "unless -d $quelle" ist keine ausreichende Prüfung, ob $quelle lesbar ist.

    Standard-Floskel:

    open FILEHANDLE,"<$filename" or die "Error reading $filename: $!";

    Eine Datei *byteweise* zu kopieren ist so ziemlich das ineffizienteste, was man programmieren kann. Lies mit der read-Funktion größere Blöcke (1024 Bytes, 4096 Bytes, 65536 Bytes - je nach dem, wie viel Speicher Du nutzen willst), sobald die read-Funktion weniger als die Blockgröße liest, hast Du das wahrscheinlich Dateiende erreicht. Ganz sauber läßt man eine while-Schleife laufen, bis die read-Funktion 0 oder undef zurückliefert. eof() brauchst Du nicht, das steht auch in der Doku. "Practical hint: you almost never need to use eof in Perl, because the input operators typically return undef when they run out of data, or if there was an error."

    my $buffer;
      while (read(QUELLE,$buffer,1024)) {
        print ZIEL $buffer;
      }

    Wenn die Speicherbelastung nebensächlich ist, geht es auch so:

    print ZIEL while <QUELLE>;

    Das ist "echtes" Perl: Kurz, knapp, effektiv, und lesbar. Übrigens auch so in der Doku zu finden. Und ja, es kopiert die gesamte Datei!

    So, das war jetzt das längste RTFM, daß ich in den letzten Wochen geschrieben habe. Tu es! Lies die exzellente Dokumentation zu Perl! Es hilft!

    Und dann versuch nicht, an Trivialitäten wie dem stumpfen Kopieren von Dateien herumzuoptimieren. Das wirst Du mit Perl ohnehin nicht schaffen. Denn wie Du der Doku von File::Copy entnehmen kannst, wird sehr oft das Betriebssystem beauftragt (Stichwort: syscopy), die Datei zu kopieren. Und das passiert bestenfalls in optimiertem Assembler-Code. Da hast Du mit reinem Perl nicht den Hauch einer Chance. Es gibt übrigens auch Module, um ganze Dateibäume zu kopieren - mit einem einzigen Funktionsaufruf.

    Wenn Du Perl lernen willst, lies den (meistens sehr gut kommentierten) Code der Module, die Perl schon beiliegen. Und den Code der Module, die Du außerdem brauchst. Das Rad tausend mal neu zu erfinden macht es nicht besser. Du bist dann einfach nur dazu verdammt, alle Fehler noch einmal zu machen, die andere längst beseitigt haben. Konzentriere Dich auf Dein Kernproblem, optimiere dort, und für den Rest gibt es gut getestete Module.

    Und erst wenn Du 100% sicher bist, daß Dein Code fehlerfrei ist und etwas trotzdem nicht funktioniert, mußt Du überlegen, ob vielleicht nicht doch ein Modul defekt ist. Auch in gut getesteten Modulen können manchmal, sehr selten, echte Design-Fehler vorkommen. Ich habe in den letzten drei Jahen gerade einmal einen gefunden: http://www.rosat.mpe-garching.mpg.de/mailing-lists/dbi/2000-02/msg00375.html (Thread verfolgen)

    Alexander

    --
    Nein, ich beantworte keine Fragen per eMail. Dafür ist das Forum da.
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so!"
    Mein "Lieblings-Forums-Bug": http://cforum.teamone.de/phpbt/bug.php?op=show&bugid=103&pos=2
    1. Benutze das w-Flag und das strict-Pragma, dann passieren solche Fehler nicht mehr:

      Das w-Flag und das strict-Pragma nutze ich. Aber Fehler oder Warnungen gab es desbezüglich noch keine.

      Du prüfst nicht, ob open() fehlschlägt. "unless -d $quelle" ist keine ausreichende Prüfung, ob $quelle lesbar ist.

      Doch ich prüfe ob open() fehlschlägt, zwar nicht so sondern ich springe mit error($quelle,$!,'von wo') zu eine sub. Ich habe das hier aber nicht mit reingeschrieben.

      Eine Datei *byteweise* zu kopieren ist so ziemlich das ineffizienteste, was man programmieren kann. ....

      Das habe ich gestern auch schon bemerk und geändert.

      So, das war jetzt das längste RTFM, daß ich in den letzten Wochen geschrieben habe. Tu es! Lies die exzellente Dokumentation zu Perl! Es hilft!

      Ja, hast recht ich sollte die Doku mal lesen.

      Wenn Du Perl lernen willst, lies den (meistens sehr gut kommentierten) Code der Module, die Perl schon beiliegen. Und den Code der Module, die Du außerdem brauchst. Das Rad tausend mal neu zu erfinden macht es nicht besser. Du bist dann einfach nur dazu verdammt, alle Fehler noch einmal zu machen, die andere längst beseitigt haben. Konzentriere Dich auf Dein Kernproblem, optimiere dort, und für den Rest gibt es gut getestete Module.

      Wie gesagt, ich weis das es Module gibt die das schnell und Fehlerfrei können. Aber vielleicht will ich ja auch die Fehler noch einmal machen, weil es mir Spass macht, anschlissend die Fehler zu suchen und daraus zu lernen.

      Danke für deine Hinweise

      MfG
      Dr. Ma-Busen