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. |