Ich habe mit der Funktion ord() versucht die einzelnen Zeichen des Strings in die dezimal-Werte umzuwandeln. Das funktioniert nur leider nicht mit den Umlauten. Für "ä" bekomme ich anstatt 228 195 raus. Gleiches Problem mit öü und ß.
Wie kann ich ansonsten den dezimal-Wert ermitteln?
Du musst den Unicode-Codepoint kennen. Den kannst du aus der UTF-8-Bytesequenz zurückrechnen oder mit iconv ermitteln. Das kennt auch Unicode. Leider ist das nicht überall verfügbar.
Ich habe mal spaßeshalber eine Dekodierroutine gebastelt, die ohne externe Hilfen auskommt. So ganz trivial, wie ich dachte, war das mitsamt Fehlerprüfungen dann doch nicht …
function utf8rtf($eingabe) {
//
// Parameter zum Erkennen und Prüfen von utf-8-Bytesequenzen
// sequenz: Anzahl der von dieser Sequenz belegten Bytes.
// startmaske: Bitmaske, um Startbyte zu erkennen bzw. invertiert, um Nutzbits aus dem Startbyte zu filtern.
// startcode: Filterung des Startbytes mit startmaske muss diesen Wert ergeben.
// min: Diese Sequenz muss mindestens den Wert min beherbergen, andernfalls hätte eine kürzere Sequenz zu benutzt werden müssen.
$utf = Array();
// $utf[] = Array("sequenz" => "1", "startmaske" => 128, "startcode" => 0, "min" => 0); - ein Byte lässt sich gesondert schneller behandeln
$utf[] = Array("sequenz" => "2", "startmaske" => 224, "startcode" => 192, "min" => 128);
$utf[] = Array("sequenz" => "3", "startmaske" => 240, "startcode" => 224, "min" => 2048);
$utf[] = Array("sequenz" => "4", "startmaske" => 248, "startcode" => 240, "min" => 65536);
// Ein mit utf-8 kodierter Wert kann theoretisch mehr als vier Bytes belegen, tut es in der Unicode-Praxis aber nie.
//
// Weitere Variablen
$ausgabe = "{\uc0"; // Erhält Ergebnis der Konvertierung. RTF-utf-8-Steuercodes folgen in diesem RTF-Block ({) keine Ersatzbytes (\uc0).
$laenge = strlen($eingabe);
//
// Die große Rundfahrt.
for ($e = 0; $e < $laenge; $e++) {
$x = false; // dekodierte Unicode-Nummer des Zeichens, false = ungültig
$c = ord($eingabe{$e}); // Wert des aktuellen Bytes
if ($c < 128) { // US-ASCII-Zeichen schnell und schmerzlos 1:1 übernehmen
$x = $c;
}
else if ($c >= 192) { // utf-8-Startbyte
//
// utf-8-Sequenz erkennen
foreach ($utf as $u) {
if (($c & $u["startmaske"]) == $u["startcode"]) { // Bytewert maskieren und mit Sequenzstartcode vergleichen
$x = $c & (~ $u["startmaske"]); // aus dem Bytewert die Nutzbits herausmaskieren, dazu dient die invertierte Startmaske
break; // Sequenz gefunden, weiter geht's unten ...
}
}
$e++; // nächste Position in der Eingabezeichenkette
//
// Folgebytes einlesen und zu $x hinzurechnen
$i = 0;
while (($e + $i < $laenge) && ((($c = ord($eingabe{$e + $i})) & 192) == 128)) { // utf-8-Folgebytes: [Byte] & 1100 0000 (192) muss 1000 0000 (128) sein, sonst gehört's nicht zur Sequenz.
if (($x !== false) && ($i < $u["sequenz"] - 1)) { // gültige utf-8-Sequenz erkannt und dieses Folgebyte wird noch erwartet
$x <<= 6; // vorhandene Bits um sechs Stellen nach links schieben und
$x += $c & 63; // neue sechs Bits in freien Platz einfügen
}
$i++;
}
//
// Gelesenen Unicode-Wert prüfen
if (($x !== false) || ($i != $u["sequenz"] - 1) || ($x < $u["min"])) { // Fehlerhafte utf-8-Bytefolge: Keine Sequenz oder zu lang bzw. zu kurz oder einen Wert in einer zu großen Sequenz geparkt.
$x = false;
}
//
// Schon mal an nächste Position in der Eingabezeichenkette springen.
$e += $i - 1;
}
//
// Ungültige Unicode-Werte. Es gibt weitere, die ich jetzt aber still und heimlich unterschlage ...
if (($x == 65534) || ($x == 65535)) {
$x = false;
}
//
// Unicode-Wert RTF-kompatibel machen
if ($x > 65535) { // RTF bzw. Word kann nur 16-Bit-Werte verarbeiten.
$x = false;
}
else if ($x > 32767) { // RTF erwartet vorzeichenbehaftete 16-Bit-Werte, d.h. im Bereich von -32768 bis +32767.
$x = 32767 - $x;
}
//
// Erkannten Unicode-Wert oder Fehlerplatzhalter ? ausgeben.
if ($x === false) { // Kein gültiger Unicode-Wert.
$ausgabe .= "?";
}
else if (($x < 128) && ($x >= 0)) { // US-ASCII unverändert ausgeben. Wegen RTF kann x auch kleiner als 0 sein.
$ausgabe .= chr($x);
}
else { // Alle anderen Zeichen gemäß RTF maskieren.
$ausgabe .= sprintf('\u%d ', $x); // Leerzeichen trennt RTF-Steuercode zum nächsten Textzeichen ab, ansonsten würde eine nachfolgende Zahl zum \u-Steuercode hinzugezählt werden.
}
}
$ausgabe .= "}"; // Eingangs mit {\uc0 begonnenen RTF-Block wieder
return $ausgabe;
}