Mit dem Modul Spreadsheet::WriteExcel
lassen sich
aus Perl heraus plattformunabhängig
Dateien für Microsofts Tabellenkalkulation Excel erzeugen.
Meine CD-Sammlung lagere ich in den praktischen Sammelboxen von Discgear ([3] und Abbildung 1) -- die sind 35cm breit und nehmen 80 CDs auf. Allerdings stinkt die mitgelieferte Software zum Himmel und ich drucke mir die Boxenbeschriftung, die die Interpreten und Titel von 80 CDs auf kleinstem Raum auflistet, selber aus.
Dies tat ich bislang dadurch, dass ich die CD-Titeldaten aus einer ASCII-Datei extrahierte und mit Cut-und-Paste nach Excel kopierte, in vier Spalten a 20 Zeilen umschichtete und anschließend von dort an den Drucker sandte.
Abbildung 1: Die Disc-Gear-Box mit dem Excel-Inhaltsverzeichnis oben im Deckel |
Dann fiel mir [2] aus dem Perl Journal
ein und dass man Dateien für Microsoft Excel auch
mit Perl erzeugen kann. Das Modul Spreadsheet::WriteExcel
von
John McNamara vom CPAN installiert sich wie immer mit
perl -MCPAN -e'install Spreadsheet::WriteExcel'
und beschränkt sich darauf, neue Excel-Dateien zu erzeugen. Es
kann keine bestehenden Dateien lesen (dafür ist Spreadsheet::ParseExcel
zuständig) oder beschreiben, und auch nicht alles,
was Win32::OLE
mit Excel anstellen kann.
Allerdings setzt Win32::OLE
im Gegensatz zu
Spreadsheet::WriteExcel
das Windows-Betriebssystem und ein
installiertes Excel voraus. Um aber nur einige Excel-Zellen
zu füllen, kommt Spreadsheet::WriteExcel
gerade recht.
Meine CD-Datenbank mit Indexsuche (die ich ein andermal vorstellen werde) kann die CD-Dateien im in Abbildung 2 gezeigten Format exportieren: ASCII, ein Eintrag pro Zeile, wobei Interpret und CD-Titel durch ein Komma getrennt stehen.
Abbildung 2: Die Rohdaten in der Datei. |
Listing toexcel
greift sich die Rohdaten, formatiert sie so, dass
in eine 20 mal 4 große Excel-Matrix passen und nutzt
Spreadsheet::WriteExcel
, um die Excel-Datei cd.xls
zu schreiben.
Kopiert man die Ausgabedatei cd.xls
danach von Linux nach Windows und
startet Excel damit, nimmt dieses, wie Abbildung 3 zeigt, das ins Nest
gelegte Ei glücklich auf, zeigt die gesetzten Spalten und Zeilen
ohne Murren an und druckt sie auf Kommando aus.
Abbildung 3: In Excel. |
Zeile 5 in toexcel
aktiviert, wie in jedem professionellen
Perlskript, mit use strict
strenge Regeln für Variablen, Referenzen
und Funktionen. Variablen müssen entweder mit my
oder our
deklariert,
oder aber über den vollen Package-Namen spezifiziert werden.
use warnings
in Zeile 6 stellt ab Perl 5.6.0
den aktiven Warnungsmodus an, der vor offensichtlich begangenen
Leichtsinnsfehlern warnt und verhindert zum Beispiel wirksam,
dass man wegen einer nicht ordentlich initialisierten Variable
stundenlang nach dem Fehler sucht.
In $IN
und $OUT
in den Zeilen 8 und 9 liegen die
Konfigurationsparameter des Skripts, die die Namen von
Ein- bzw. Ausgabedatei festlegen.
Die while
-Schleife ab Zeile 17 liest die in Abbildung 3 gezeigte
Datei zeilenweise ein. Der anschließende
chomp
-Befehl schneidet den jeder Zeile
anhängenden Zeilenumbruch ab. split()
in Zeile 19 spaltet
die Zeile am Komma in Interpret und Titel. Der letzte Parameter
2
gibt an, dass die Zeile maximal in zwei Felder gespalten wird,
auch wenn mehr als ein Komma darin vorkommt. Falls also der
CD-Titel ein Komma enthält, spaltet split()
die Datenzeile trotzdem
in Interpret und Titel, da es nach dem ersten gefundenen Komma
den split
-Vorgang abbricht.
Sollte eine Zeile allerdings kein Komma enthalten, steht also nur
der Interpret da, bleibt $title
auf dem Wert undef
. Wegen
des strengen Warnungsmodus hätte dies allerdings hässliche
Meldungen zur Folge, weswegen Zeile 20 den eventuell undefinierten
Skalar $title
auf den Leerstring setzt. Das Konstrukt
$title ||= ""
ist dabei eine Perl-Abkürzung für
$title || $title = "";
Falls also $title
falsch oder undefiniert ist, wird der rechte
Teil der logischen Oder-Verknüpfung ausgeführt und $title
mit dem Leerstring besetzt. Sollte irgendwann mal eine CD
mit dem Title "0"
herauskommen, ginge das natürlich voll
in die Hose, da "0"
logisch falsch ist -- aber das kann
ich riskieren.
Die gefundenen Interpreten/Titel-Paare speichert toexcel
bis
zur späteren Abarbeitung in dem Array @entries
. Da jedes
Element von @entries
aber aus zwei Unterelementen
(Interpret und Titel) besteht, formt Zeile 21 aus dem Wertepaar mit
den eckigen Klammern [...]
einen anonymen Array und puscht
eine Referenz darauf auf den Array @entries
.
Ab Zeile 26 wütet dann Spreadsheet::WriteExcel
und erzeugt
zunächst ein Workbook-Objekt in der Excel-Welt, dessen
addworksheet()
-Methode in Zeile 27 ein Excel-Worksheet
anlegt. Auf diesem ``Arbeitsblatt'' stehen später die Zeilen
und Spalten des Spreadsheets.
Für spezielle Formatangaben, die Fonts, deren Größe oder
andere Gestaltungsparameter festlegen,
dienen in Excel Formate, die Spreadsheet::WriteExcel
über
Formatobjekte definiert. Die add_format()
-Methode
eines Workbook-Objekts erzeugt ein neues Format. Sie nimmt Argumente in
der Form Parametername, Wert entgegen. Zeile 28 stellt
den Font Arial in der Größe 8 ein.
Um die 80 Zeilen der Ausgangsdatei in eine Matrix von 20 Zeilen
mal 4 Spalten zu übersetzen, rattern die for
-Konstrukte
in den Zeilen 33 und 35 von Zeile 0
bis 19
und Spalte 0
bis 3
.
Jedesmal holt Zeile 37 eine Referenz auf einen Unterarray aus
@entries
, der wiederum Interpret und Titel der aktuellen
CD als Elemente enthält. Gibt's keine CDs mehr, bricht Zeile
39 die for
-Schleife ab.
Zeile 41 macht aus der Unter-Arrayreferenz $e
mittels
@$e
wieder ein Array
und weist dessen Elemente den Skalaren $artist
und $titel
zu.
Die write()
-Methode des Worksheet-Objekts in Zeile
44 nimmt den Zeilen- und Spaltenindex der Excel-Zelle entgegen, in
die es den Text des dritten Parameters schreiben wird.
Der vierte Parameter bestimmt das verwendete Format.
Eigentlich besteht unser Worksheet entsprechend Abbildung 3 aber
nicht aus 4 Spalten, sondern aus 8 -- jeder Doppelspalteneintrag führt
nämlich eine laufenden Nummer (von 1 bis 80) und rechts daneben
den eigentlichen Text. Der Spaltenindex der Nummer ist dementsprechend
2 * $col
und der des Texts ist 2 * $col + 1
, wenn $col
der Index
der ursprünglich angenommenen 'virtuellen' Kolumne ist.
So schreibt Zeile 44 die laufende Nummer des Eintrags und Zeile 47 den Eintrag selbst, der aus Interpret und CD-Titel besteht, die beide durch einen Gedankenstricht getrennt werden.
Um anschließend die Breite der Nummernspalte auf 2
und die der Eintragsspalte auf 20 zu setzen, iteriert
das for
-Konstrukt ab Zeile 52 noch einmal über alle
Spalten und legt mittels der set_column()
-Methode ihre
Eigenschaften fest.
set_column()
nimmt Anfangs- und Endindex eines Spaltenbereichs,
sowie die gewünschte Spaltenbreite entgegen. Um zum Beispiel
nur die Spalte 0
des Spreadsheets auf die Breite 2
zu stellen,
ist der Aufruf
$sheet->set_column(0, 0, 2);
erforderlich. Zeile 59 schreibt mit der close()
-Methode des
Excel-Workbook-Objekts das Spreadsheet in die Ausgabedatei und
schließt den Vorgang ab. Ganz einfach -- dank schlauer Modultechnik
vom CPAN! Konvertiert fleißig zwischen den Welten!
01 #!/usr/bin/perl 02 ################################################## 03 # toexcel --Mike Schilli, 2001 (m@perlmeister.com) 04 ################################################## 05 use strict; 06 use warnings; 07 08 my $IN = "cd2.dat"; # Eingabedatei 09 my $OUT = "cd.xls"; # Ausgabe-(Excel)-Datei 10 11 use Spreadsheet::WriteExcel; 12 13 open IN, "<$IN" or die "Cann open $IN"; 14 15 my @entries = (); 16 17 while(<IN>) { 18 chomp; 19 my($artist, $title) = split /-/, $_, 2; 20 $title ||= ""; 21 push @entries, [$artist, $title]; 22 } 23 24 close IN; 25 26 my $book = Spreadsheet::WriteExcel->new($OUT); 27 my $sheet = $book->addworksheet(); 28 my $format = $book->addformat(font => "Arial", 29 size => 8); 30 31 my $count = 0; 32 33 for my $col (0..3) { 34 35 for my $row (0..19) { 36 37 my $e = shift @entries; 38 39 last unless $e; # Keine Einträge mehr? 40 41 my($artist, $title) = @$e; 42 43 # Zähler schreiben 44 $sheet->write($row, 2*$col, ++$count, 45 $format); 46 # Eintrag schreiben 47 $sheet->write($row, 2*$col+1, 48 "$artist - $title", $format); 49 } 50 } 51 52 for my $col (0..3) { 53 # Zählerfeld: 2 Zeichen breit 54 $sheet->set_column(2*$col, 2*$col, 2); 55 # CD-Feld: 20 Zeichen breit 56 $sheet->set_column(2*$col+1, 2*$col+1, 20); 57 } 58 59 $book->close();
Michael Schilliarbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com. |