Donnerstag, 13. September 2012

Groovy: Mit Files und Texten effizient arbeiten

Mit groovy kann man sehr schon mit Files und Texten arbeiten. Aber es gibt auch ein paar Stolpersteine. Damit man sicher zum Ziel kommt, hier ein kleines Tutorial.
In diesem Script wird eine Datei geladen und die doppelten Zeilen gelöscht, sowie der Inhalt sortiert. Das Ergebnis wird dann in eine andere Datei mit gleichem Namen aber einer anderen Endung (.txt) gespeichert.
fName = "xyz.csv"
infile = new File(fName)
outfile = new File(fName[0..-4]+"txt")
erg = new HashSet()
infile.eachLine { erg.add(it) }
outfile.write('');
erg.sort().each{
    outfile.append(it+"\n", "UTF-8");
}

java.io und java.util sind schon drin
Das Script ist vollständig. Auch ohne imports. Der Grund: Groovy hat in groovy.lang die Pakete io und util dabei. Eine Kleinigkeit, aber für Java Umsteiger doch bemerkenswert.

File ist nicht gleich java.io.File
Eigentlich doch, nur das groovy die Klasse File erweitert hat um einige Convience Funktionen. Im Beispiel sieht man z.B. eachLine was eine Closure erwartet, die dann auf jede Zeile angewendet wird.  Was in Java lästig ist: Das recht alte java.io Paket wirft mit Exceptions um sich. Gerade für ein Script ist das lästig. Da hat groovy ganz einfach dafür gesorgt, dass man sich darum nicht kümmern muss. Obwohl es in groovy auch einen sehr guten try catch Mechanismus gibt, ist per default kein Exception Handling nötig. Zu bemerken ist noch, das ein explizites close auf die Dateien hier nicht notwendig ist, da groovy Files automatisch frei gibt.

Strings sind auch Arrays und Arrays sind anders 
Viele Java Programmierer werden bei der 3. Zeile stocken. Denn in groovy kann ein String als Array angesehen werden. D.h. man kann die Buchstaben im String einfach per Array Index adressieren. Damit nicht genug, die letzte Stelle in einem Array ist die -1. D.h wenn man den 3 Buchstaben des Datei Suffix adressieren will, findet man den auf der Position -4. Jetzt kommt noch eine weitere Erweiterung von groovy ins Spiel, das bilden von Subarrays. Dazu kann man einfach wie im Beispiel [0..-4] schreiben, um den Substring "alles außer die letzten 3 Zeichen" zu extrahieren.

Encoding raten und die Nebeneffekte
Ein wichtiger Designentscheid von Groovy ist Convience: Code less. Nur in selten Fällen kann es in die andere Richtung gehen. Das CharsetToolkit ist so ein Kandidat. Es wird z.B. beim lesen einer Datei das encoding geraten in dem die ersten Zeilen gelesen werden und daraus ein Charset ermittelt wird. Wenn das nicht geht, z.B. bei Ausgabefiles wird der System Default Charset verwendet. Da in meinem Beispiel aber der Text im inFile UTF-8 codiert ist, wird er entsprechend auch so in der Ausgabe erwartet. Darum muss das Charset, wie oben beschrieben, explizit angegeben werden.

Strings und Charakter sind eins, nur für Java nicht
Auch und obwohl ich es gut finde, das groovy nicht zwischen chars und Strings unterscheidet, gibt es beim Wechsel von groovy code zu Java Bibliotheken einen Stolperstein. Wird als Parameter einer Java Methode ein char erwartet, dann findet groovy die Methode nicht. Die Folge ist, das man groovy ein wenig auf die Sprünge helfen muss. Kleines Beispiel gefällig:


import org.apache.commons.csv.*

// ok
new CSVParser(new FileReader(new File(fName)),
       new CSVStrategy(';' as char,'"' as char,'#' as char))

// geht nicht
new CSVParser(new FileReader(new File(fName)),
       new CSVStrategy(';','"','#' ))

Wenn man es weiss, ist es erklärlich. Bis man es gefunden hat, kann  man schon an sich Zweifeln...

Keine Kommentare:

Kommentar veröffentlichen