Heute mal ein wenig Ruby. Wir wollen die JLPT-Dateien des EDICT-Projekts so umwandeln, das man die Datei leicht in eine Filemaker-Datenbank importieren kann. Man könnte die Umwandlung auch in Filemaker selbst vornehmen, allerdings ist das deutlich komplizierter.
Das Standardformat der JLPT-Dateien ist
Schreibung [Lesung] /Bedeutung1/Bedeutung2.../Bedeutungen/
Es kommt also erst die Schreibung, dann die Lesung in eckigen Klammern und schließlich die englischen Übersetzungen in Slashes.
Um eine Datei zum Lesen zu öffnen kann man in Ruby folgendes schreiben:
input = File.open("jlpt-voc-4-extra.utf")
Die Datei kann man jetzt über input ansprechen. Eine Ausgabedatei brauchen wir auch noch, also machen wir
output = File.open("jlpt-converted.tab","w:UTF-16LE")
Wir wollen eine tabseparierte Ausgabedatei erstellen, weil diese leicht von Filemaker importiert werden können. Als Ausgabekodierung setzen wir UTF-16.
Nun müssen wir die Datei Zeilenweise durchgehen und in die Einzelteile auftrennen. Die Datei-Klasse von Ruby kann hierfür each verwenden. Ein Beispiel:
input.each do |line| puts line end
Dieser Code würde jede Zeile einmal ausgeben. Puts gibt das aus was ihm übergeben wird und beginnt eine neue Zeile.
Die Schreibung ist alles was sich links vom ersten Leerzeichen befindet. Wenn wir die Zeile an den Leerzeichen in kleine Teile zerhacken könnten wäre es also das erste Stückchen. Genau das macht der split-Befehl. Unser neues Beispiel:
input.each do |line|
schreibung = line.split(" ")[0]
puts schreibung
end<
Nun gibt unser Code die Schreibung aus! line.split(" ") gibt ein Array zurück, also eine Liste von Stückchen. Diese Stückchen lassen sich über [Nummer] ansprechen. Da bei 0 angefangen wird zu zählen ist das erste Stückchen also [0].
Die Lesung zu filtern ist schon etwas schwerer. Die Lösung:
input.each do |line|
schreibung = line.split(" ")[0]
lesung = line[/\[(.*)\]/,1] || schreibung
puts schreibung + " " + lesung
end
Das sieht kompliziert aus, ist aber eigentlich auch einfach. Das zwischen den eckigen Klammern bei line ist ein regulärer Ausdruck. Reguläre Ausdrücke sind speziell dafür gemacht um Textstücke herauszufiltern. In Ruby sind reguläre Ausdrücke immer von Slashes (/Ausdruck/) umgeben, der eigentliche Ausdruck in unserem Fall ist also \[(.*)\]. In regulären Ausdrücken passen bestimmt Zeichen auf bestimmte andere Zeichen. Wenn ein Zeichen passt wird es herausgefiltert. Alphanumerische Zeichen passen auf sich selbst. Beispiele:
"Hallo, wie geht's?"[/Hallo/] => "Hallo" "Hallo, wie geht's?"[/llo/] => "llo" "Hallo, wie geht's?"[/Bundespräsident Horst Köhler/] => nil
Gibt es keine passenden Zeichen gibt der Ausdruck nil zurück, was in Ruby "kein Ergebnis" bedeutet. Uns interessieren aber nun beliebige Zeichen, die sich zwischen zwei eckigen Klammern befinden. Wir wollen also etwas wie [irgendwas]. Leider habe eckige Klammern schon eine besondere Bedeutung in regulären Ausdrücken, d.h. sie passen nicht auf sich selbst. Eckige Klammern passen auf \[ und \]. Unser neuer Ausdruck ist also \[irgendwas\]. Jetzt müssen wir nur noch irgendwas als regulären Ausdruck schreiben. Wir wollen eine beliebige Anzahl von beliebigen Zeichen filtern. Ein Punkt (also".") passt auf genau ein beliebiges Zeichen. "[a]"[/\[.\]/] würde also "[a]" zurückgeben. Auf "[aaaa]" würde es aber schon nicht mehr passen! Zum Glück gibt es auch den Ausdruck *, der "beliebig viele vom vorherigen Zeichen" bedeutet. .* bedeutet also "beliebig viele beliebige Zeichen. Genau was wir brauchen! [/\[.*\]/] passt also auch auf [わたし] und gibt in diesem Fall das selbe zurück."赤い [あかい] /(adj) red/(P)/"[/\[.*\]/] würde "[あかい]" zurückgeben. Um die Klammern los zu werden machen wir eine Gruppe aus den "beliebigen Zeichen", indem wir normale Klammern um sie herum machen. Jetzt gibt uns der Ausdruck mehrere Gruppen zurück. Mit ",1" sagen wir ihm, das wir die zweite Gruppe haben wollen. Die erste Gruppe ist alles was passt (also "[あかい]") und hat die Nummer 0. Unsere zweite hat die Nummer 1, also ist unser endgültiger Ausdruck [/\[(.*)\]/] und wird so verwendet:
lesung = line[/\[(.*)\]/,1]
Manche Einträge haben aber in der JLPT-Datei gar keine Lesung! Katakana-Wörter stehen nur so da, unser Ausdrück würde also nil zurückgeben, da keine passenden Stücke gefunden werden. Dafür ist nun || schreibung da. Es bedeutet "Wenn das vorherige nil war, dann nimm das folgende". Wenn also keine Lesung in der JLPT-Datei steht wird einfach die Schreibung als Lesung genommen. Wenn man sich nun noch mal den Code oben ansieht wird man merken dass er gar nicht so schwer ist wie zunächst gedacht. Die Bedeutung bekommt man genau so heraus, darum hier das fertige Programm:
input = File.open("jlpt-voc-4-extra.utf")
output = File.open("jlpt-converted.tab","w:UTF-16LE")
input.each do |line|
schreibung = line.split(" ")[0]
lesung = line[/\[(.*)\]/,1] || schreibung
bedeutung = line[/\/(.*)\//,1]
output.puts "#{schreibung}\t#{lesung}\t#{bedeutung}" unless line[0] == "#" || line.strip == ""
end
output.close
input.close
puts wird nun auf output angewendet und schreibt darum alles in eine Datei. \t ist das Zeichen für Tabulator und das ganze wird nur in die Datei geschrieben wenn das erste Zeichen nicht "#" ist oder die ganze Zeile leer ist.
No Comments