Viele Zeichensätze sind untereinander inkompatibel, weil nicht jeder Zeichensatz den gleichen Zeichenumfang besitzt. Beim Konvertieren von Zeichensatz X zum Zeichensatz Y kann es also passieren, dass Zeichen im Zielzeichensatz nicht vorhanden sind und deswegen nicht korrekt dargestellt werden.
In diesem Artikel zeige ich, wie man das Problem mittels Ruby und
I18n.transliterate
lösen kann.
Iconv
Iconv ist eine Schnittstelle zum Konvertieren von Zeichenketten von einer Kodierung in eine andere.
In dem folgenden Beispiel wird die Zeichenkette von UTF-8 nach CP1252 konvertiert. Nicht vorhandene Zeichen werden durch Ersatzzeichen repräsentiert (transliterate).
$ require 'iconv'
true
$ Iconv.conv('CP1252//translit', 'UTF-8', 'Český Krumlov')
"Cesk\xFD Krumlov"
Aus Č
wird ein C
und aus ý
wird \xFD
(\xFD
= ý
in CP1252).
Iconv ist seit Ruby 1.9.3 “deprecated” und wurde in Ruby 2.0.0 entfernt. Man benötigt also einen Ersatz. Hallo I18n.
I18n
I18n ist eine Ruby-Bibliothek zur Internationalisierung.
#transliterate
Die Methode #transliterate
ersetzt Nicht-ASCII-Zeichen durch ein ähnliches
ASCII-Zeichen. Wie das konkret aussieht, sieht man im folgenden Beispiel:
$ require 'i18n'
true
$ I18n.transliterate('Český Krumlov')
"Cesky Krumlov"
Aus Č
wird ein C
und aus ý
ein y
.
Von UTF-8 zu CP1252
Würde man in unserem Beispiel #encode
aufrufen, um die Zeichenkette von UTF-8
in CP1252 zu konvertieren, erhält man folgende Fehlermeldung, da nicht alle
Zeichen im CP1252-Zeichensatz enthalten sind:
$ 'Český Krumlov'.encode('CP1252', 'UTF-8')
Encoding::UndefinedConversionError: U+010C to WINDOWS-1252 in conversion from
UTF-8 to WINDOWS-1252
Jetzt kommt I18n.transliterate
zum Einsatz. Ich habe eine kleine Methode
geschrieben, die #encode
und #transliterate
kombiniert. Natürlich können
die Zeichensätze beliebig ausgetauscht werden.
require 'i18n'
def convert_utf8_to_cp1252_with_transliteration(str)
str.chars.map do |char|
# Verwendet Ersatzzeichen für alle Zeichen, die nicht in CP1252 enthalten
# sind.
char.encode('CP1252', 'UTF-8', invalid: :replace, undef: :replace,
replace: I18n.transliterate(char))
end.join
end
Ich iteriere über jedes Zeichen und versuche, dieses in den angegebenen
Zeichensatz zu konvertieren. Wenn das Zeichen nicht in dem Zeichensatz vorhanden
ist, wird I18n.transliterate
aufgerufen und das Zeichen mit einem ähnlichen
ersetzt.
$ convert_utf8_to_cp1252_with_transliteration('Český Krumlov')
"Cesk\xFD Krumlov"
Mittels dieser Methode erhält man wieder das gleiche Ergebnis, wie in dem am Anfang gezeigten Beispiel mit Iconv.