Mac Perl Primer

Ausgabe auf Datei

Unser Droplet aus dem letzten Kapitel war ja ganz nett, aber viel komfortabler wäre es doch, wenn wir nicht nur eine Datei lesen können, sondern auch eine neue Datei mit dem Ergebnis erzeugen können.

Dazu müssen wir aber zunächst überlegen, in welche Datei unser Programm schreiben soll. Es gibt dazu folgende Möglichkeiten:

  1. Wir schreiben in eine Datei mit festen Namen, z.B. "table.html". Im Finder können wir sie dann gegebenfalls umbenennen.
  2. Das Konvertierungsprogramm fragt uns
  3. An den Namen der Eingabedatei wird eine Endung angehängt.
  4. Die Eingabedatei wird gnadenlos ersetzt.

Wir werden alle Möglichkeiten untersuchen und fangen mit der einfachsten an. Dazu nehmen wir uns zunächst ein ganz einfaches Beispiel:

open( OUTPUT, ">meinedatei" );
print OUTPUT "Hallo du kleine Datei";
close( OUTPUT );

Uns fällt natürlich als erstes das groß geschriebene Wort OUTPUT auf. Es handelt sich hier um ein Filehandle ( Dateianfasser ). Die Großschreibung ist nicht Pflicht, aber allgemeine Konvention. Erstaunlicherweise steht vor diesen Filehandle kein Dollar oder Klammeraffe oder anderes Sonderzeichen. Das Filehandle ist für alles wichtig, was sich auf den Inhalt der Datei bezieht.

So ein Handle ist nur sinnvoll wenn man etwas hat, was man damit anfassen kann. Die Zuordnung passiert mit dem Befehl open. Die Datei "meinedatei" wird mit mit dem Filehandle OUTPUT verbunden. Das ">" Zeichen zeigt an, das wir in Datei hineinschreiben wollen, es ist wie ein Pfeil zu lesen, der in die Datei hineinzeigt. Falls es die Datei noch nicht gibt, wird Sie angelegt, falls es die Datei schon gibt, wird sie ohne Vorwarnung überschrieben.

Dann können wir mit print OUTPUT etwas in die Datei hineinschreiben, so wie wir mit print etwas in das MacPerl Fenster schreiben. Achtung: Zwischen Output und dem was ausgegeben wird steht kein Komma.

Mit close wird dann die Verbindung zwischen dem Handle und der Datei aufgehoben, ab jetzt darf man ohne Risiko mit anderen Programmen auf die Datei zugreifen.

Die Datei "meinedatei" wird im für MacPerl aktuellen Verzeichnis erzeugt. Was das genau bedeutet, hängt auch von der Einstellung des MacOS ab. Im Zweifelsfall muß die Datei mit "Dateien finden" gesucht werden.

Ebenfalls eher Zufall ist, welches Programm startet wenn wir unsere Datei doppelklicken. Im Gegensatz zu anderen Betriebssystemen hat auf den Macintosh jede Datei eine Kennung, die den Dateityp und die zugehörige Applikation verrät.

Diese haben wir bisher noch nicht spezifiziert. Hierzu haben wir folgende Möglichkeiten:

MacPerl::SetFileInfo("ttxt","TEXT","meinedatei"); # simpletext
MacPerl::SetFileInfo("McPL","TEXT","meinedatei"); # MacPerl 
MacPerl::SetFileInfo("ALFA","TEXT","meinedatei"); # Alpha Editor 
MacPerl::SetFileInfo("R*ch","TEXT","meinedatei"); # BBedit Editor
MacPerl::SetFileInfo("MOSS","TEXT","meinedatei"); # Netscape

Unser Programm aus der letzten Version können wir z.B. so umschreiben:

open( OUTPUT, ">meinedatei.html" );
 
print OUTPUT "<html><head>\n";
print OUTPUT "<title>Database Publishing</title>\n";
print OUTPUT "</head><body>\n";
 
print OUTPUT "<TABLE>\n";
 
#-- Now do the Action
while(<>) {
    chop;  # remove the newline char
    @data = split /\t/;
    print OUTPUT "<TR>";
    foreach $field (@data) {
        print OUTPUT "<TD>$field</TD>";
    }
    print OUTPUT "</TR>\n";
}
 
print OUTPUT "</TABLE>\n";
print OUTPUT "</body></html>\n";
close OUTPUT;
MacPerl::SetFileInfo("MOSS","TEXT","meinedatei");# set Filetype to Netscape

Dateiausgabe nach Benutzerfrage

Eine Dialogbox mit Frage an den Benutzer erzeugen wir folgendermassen:

require "StandardFile.pl";
 
$meinedatei = StandardFile::PutFile("schreibe in Datei", "ohne Titel");
 
open ( OUTPUT, ">$meinedatei");
print OUTPUT "Hallo kleine Datei";
close OUTPUT;
 
MacPerl::SetFileInfo("MOSS","TEXT",$meinedatei);

Automatische Erzeugung von Endungen

Dazu müssen wir etwas ausholen: Dank der Droplets können wir von der Datei lesen, als ob es die Standardeingabe wäre. Dummerweise hat die Standardeingabe keinen Dateinamen, oder zumindestens keinen den wir haben wollen. Wir müssen also den Original Dateinamen erfahren. Glücklicherweise stellt MacPerl nicht nur einen, sondern bei Bedarf auch mehrere Dateinamen, die in einen Schwung auf das Droplet gezogen werden, in eine spezielle Liste mit Namen @ARGV. Das heißt, wir können z.B. gleich 20 Dateien auswählen und in einen Rutsch konvertieren.

Dann ist es natürlich schön, wenn wir nicht bei jeder Datei nach einen Ausgabenamen gefragt werden.

Um die Dateien getrennt zu halten, lesen wir aber nicht von der Standardeingabe, sondern öffnen jede Datei explizit. Unser Konvertierungsprogramm sieht dann so aus:

foreach $filename (@ARGV) {                   # für jede Datei:
    open( INPUT, $filename);                  # öffne Eingabedatei
    open( OUTPUT, ">$filename.html");         # erzeuge und öffne Ausgabedatei
 
   # jetzt koennen wir mit der eigentlichen Konvertierung anfangen
 
    while(<INPUT>) {                          # für jede Eingabezeile
        chop;                                 # entferne letztes Zeichen
        @data = split /\t/;
        print OUTPUT "<TR>";
        foreach $field (@data) {
            print OUTPUT "<TD>$field</TD>";
        }
        print OUTPUT "</TR>\n";
    }                                         #end while
 
    close( OUTPUT);
    close( INPUT );
    MacPerl::SetFileInfo("MOSS","TEXT","$filename.html");# set Filetype to Netscape
}                                             #end foreach

Um dieses Programm laufen zu lassen, muß man es als Droplet sichern und eine oder mehrere Eingabedateien darauf ziehen.

Ersetzen der Eingabedatei

Oft wollen wir eine Datei dauerhaft verändern und das Original interessiert uns nie wieder. Zum Beispiel wenn wir ein Programm schreiben, daß alle Vorkommen eines Wortes durch ein anderes ersetzt. Wir wollen dann nur noch die korrigierte Version unserer Datei haben. Jetzt könnten wir die Idee haben, es im Prinzip wie oben zu machen, nur daß wir diesmal die gleiche Datei sowohl zum Lesen als auch zum Schreiben öffnen.

ACHTUNG: Fehlerhaftes Programm:

print "Warnung: dieses Programm frisst Dateien !!\n";
 
foreach $filename (@ARGV) {                   # für jede Datei:
    open( INPUT, $filename);                  # öffne Eingabedatei
    open( OUTPUT, ">$filename");     # FALSCH !!
 
    while(<INPUT>) {                          # für jede Eingabezeile
        s/dumm/unklug/;                       # ersetze dumm durch unklug
        s/Mülltonnen/Wertstoffbehälter/;
        s/Technik/Technologie/;
        print OUTPUT $_;                      # schreibe veraenderte Zeile auf Datei
 
    }                                         #end while
    close( OUTPUT);
    close( INPUT );
}                                             #end foreach

Leider zieht man sich damit buchstäblich den Teppich unter den Füßen weg: in dem Moment, wo wir die erste Zeile schreiben, haben wird im allgemeinen unsere alte Datei zerstört. Unklug!

Statt dessen sollten wir die alte Datei zunächst umbenennen, dann konvertieren, und schließlich, nachdem wir die Datei nicht mehr brauchen, die umbenannte Datei löschen.

Korrigierte Version:

 
foreach $filename (@ARGV) {                   # für jede datei:
    $inputname = $filename . ".bak";
    rename($filename, $inputname );
 
    open( INPUT, $inputname);                  # öffne Eingabedatei
    open( OUTPUT, ">$filename");               # erzeuge und oeffne Ausgabedatei
 
    while(<INPUT>) {                          # für jede Eingabezeile
        s/dumm/unklug/;                       # ersetze dumm durch unklug
        s/Mülltonnen/Wertstoffbehälter/;
        s/Technik/Technologie/;
        print OUTPUT $_;                      # schreibe Eingabezeile auf Datei
 
    }                                         #end while
    close( OUTPUT);
    close( INPUT );
    unlink( $inputname );                     #delete file
}                                             #end foreach

Zu beachten ist: wenn wir eine Datei "meintext" und eine Datei "meintext.bak" haben, so überschreibt dieses Programm gnadenlos die Datei "meintext.bak". Ausserdem können schreckliche Dinge passieren, wenn der Dateiname 31 Zeichen lang ist und das rename deshalb fehlschlägt.

Deshalb korrigieren wir unser Programm noch einmal:

 
foreach $filename (@ARGV) {                   # für jede datei:
    $inputname = $filename . ".bak";
    if (-e $inputname) {                      # teste ob Datei existent
       MacPerl::Answer("Datei $inputname existiert schon. Bitte loeschen");
       next;                                   # vergess diese Datei, nimm die naechste
    }
    if( not rename($filename, $ inputname ) ) { 
       MacPerl::Answer("Datei $filename kann nicht umbenannt werden");
       next;                                   # vergess diese Datei, nimm die naechste        
    }
 
    open( INPUT, $inputname);                  # öffne Eingabedatei
    open( OUTPUT, ">$filename");               # erzeuge und oeffne Ausgabedatei
 
    while(<INPUT>) {                          # für jede Eingabezeile
        s/dumm/unklug/;                       # ersetze dumm durch unklug
        s/Mülltonnen/Wertstoffbehälter/;
        s/Technik/Technologie/;
        print OUTPUT $_;                      # schreibe Eingabezeile auf Datei
 
    }                                         #end while
    close( OUTPUT);
    close( INPUT );
    unlink( $inputname );                     #delete file
}                                             #end foreach

<- vorherige Lektion

>- nächste Lektion


home - contact - q-zone - dev-zone