PHP: Codepoints ermitteln und umgekehrt die UTF-8-Bytes erzeugen

Erzeuge die Binärzeichen aus gegebenen Codepoints oder ermitteln der Codepoints aus dem Binärstring

Leider ist die Unicode-Unterstützung in PHP noch nicht soweit fortgeschritten wie in Perl, womit das alles über pack()/unpack() mit der U-Schablone erledigt werden kann:

# Codepoints ausgeben
use utf8;
print "@{[unpack 'U*', '€äöüß…']}"; # 8364 228 246 252 223 8230

# erzeuge die binary aus einer Liste mit Codepoints
use bytes;
print pack('U*', 8364, 228, 246, 252, 223, 8230);

Untenstehend eine einfache PHP-Klasse, funktional für UTF-8-Zeichen bis vier Bytes. Der Algorithmus, die erforderlichen Bit-Operationen wurden aus der JavaScript-Library stringview.js (OpenSource, Github) entlehnt.

Die kleine Klasse, untenstehend, hat zwei Funktionen bzw. Methoden.

codepoints('äöü€ß');

Übergeben werden Rohdaten, Bytes, also wie obenstehend. Das kann aus einer Datei sein oder aus einer Benutzereingabe. Die Methode gibt ein Array zurück mit den Codepoints in Dezimalschreibweise, Beispiel:

Array(
    [0] => 228
    [1] => 246
    [2] => 252
    [3] => 8364
    [4] => 223
)

Funktionsweise: Die eingegeben Zeichen liegen als Rohdaten vor, daraus wird mit unpack() ein Array mit den, den Bytes entsprechenden Oktettenwertigkeiten erstellt. Beginnend mit dem ersten Byte, wird die Wertigkeit ermittelt, daraus leitet sich dann ab, wieviele der folgenden Bytes zu einem Zeichen gehören, der Index wird entsprechenend erhöht und es wird der Codepoint berechnet.

binary(array(0x20AC, 228));

Übergeben wird eine Liste mit Codepoints, diese können sowohl hexadezimal als auch dezimal notiert sein. Mit dem gezeigten Beispiel liefert die Methode die zum Eurozeichen gehörigen 3 Bytes, gefolgt von 2 Bytes für das kleine 'ä'.

Funktionsweise: Der numerische Wert des Codepoints bestimmt, wieviele Bytes zu einem UTF-8-kodierten Zeichen gehören. Die Oktettenwertigkeiten werden berechnet und die Bytes mit der Funktion pack() erzeugt.

Code für die hier vorgestellte Klasse

<?php

class Codepoint{
    private function examine($part){
        static $idx = 1;
        if($part > 239 && $part < 248){   // 4 bytes
            $this->CODEPOINTS[] = ($part - 240 << 18) + ($this->OCTS[$idx + 1] - 128 << 12) + ($this->OCTS[$idx + 2] - 128 << 6) + $this->OCTS[$idx + 3] - 128;
            $idx += 4;
        }
        elseif( ($part > 223) && ($part < 240)){ // 3 bytes
            $this->CODEPOINTS[] = ($part - 224 << 12) + ($this->OCTS[$idx + 1] - 128 << 6) + $this->OCTS[$idx + 2] - 128;
            $idx += 3;
        }
        elseif($part > 191 && $part < 224){  // 2 bytes
            $this->CODEPOINTS[] = ($part - 192 << 6) + $this->OCTS[$idx + 1] - 128;
            $idx += 2;
        }
        else{ // 1 byte, ASCII
            $this->CODEPOINTS[] = $part;
            $idx += 1;
        }
        return $idx;
    }

    // from binary to codepoints
    public function codepoints($str){
        $this->OCTS = unpack('C*', $str);
        $this->CODEPONTS = array();
        for($i = 1; $i <= sizeof($this->OCTS);){
           $i = $this->examine($this->OCTS[$i]);
        }
        return($this->CODEPOINTS);
    }

    // erzeuge die binary
    private function concat($cp){
        if($cp < 0x80){         // 1 byte, ASCII
            $this->BIN .= pack('C', $cp);
        }
        elseif($cp < 0x800 ) {  // 2 bytes
            $this->BIN .= pack('CC', 0xc0 + ($cp >> 6), 0x80 + ($cp & 0x3f));
        }
        elseif($cp < 0x10000){  // 3 bytes
            $this->BIN .= pack('CCC', 0xe0 + ($cp >> 12), 0x80 + (($cp >> 6) & 0x3f), 0x80 + ($cp & 0x3f ));
        }
        elseif($cp < 0x200000){ // 4 bytes
            $this->BIN .= pack('CCCC', 0xf0 + ($cp >> 18), 0x80 + (($cp >> 12) & 0x3f), 0x80 + (($cp >> 6) & 0x3f), 0x80 + ($cp & 0x3f));
        }
    }

    // from codepoint to binary
    public function binary($cps){
        $this->BIN = '';
        for($i = 0; $i < sizeof($cps); $i++){
            $this->concat($cps[$i]);
        }
        return $this->BIN;
    }
}

$c = new Codepoint();
echo $c->binary(array(0x20ac, 228)), "\n";
print_r($c->codepoints('€äöü'));


?>

Datenschutzerklärung: Diese Seite dient rein privaten Zwecken. Auf den für diese Domäne installierten Seiten werden grundsätzlich keine personenbezogenen Daten erhoben. Das Loggen der Zugriffe mit Ihrer Remote Adresse erfolgt beim Provider soweit das technisch erforderlich ist. s​os­@rolf­rost.de.