Znak Unicode w łańcuchu PHP

164

To pytanie wygląda żenująco prosto, ale nie udało mi się znaleźć odpowiedzi.

Jaki jest odpowiednik PHP dla następującego wiersza kodu C #?

string str = "\u1000";

Ten przykład tworzy ciąg z pojedynczym znakiem Unicode, którego „Wartość liczbowa Unicode” wynosi 1000 szesnastkowo (4096 dziesiętnie).

To znaczy, w jaki sposób w PHP mogę utworzyć ciąg z pojedynczym znakiem Unicode, którego „wartość liczbowa Unicode” jest znana?

Telaclavo
źródło
4
@diEcho: to tylko do dopasowywania znaków Unicode, ale OP chce tworzyć do tych znaków.
Stefan Gehrig

Odpowiedzi:

178

Ponieważ JSON bezpośrednio obsługuje \uxxxxskładnię, pierwszą rzeczą, która przychodzi mi do głowy, jest:

$unicodeChar = '\u1000';
echo json_decode('"'.$unicodeChar.'"');

Inną opcją byłoby użycie mb_convert_encoding()

echo mb_convert_encoding('က', 'UTF-8', 'HTML-ENTITIES');

lub skorzystaj z bezpośredniego mapowania między UTF-16BE (big endian) a punktem kodowym Unicode:

echo mb_convert_encoding("\x10\x00", 'UTF-8', 'UTF-16BE');
Stefan Gehrig
źródło
9
JSON to nie JavaScript.
Gumbo
4
@Gumbo: Wiem o tym, ale tutaj nie ma to znaczenia. JavaScript, a także JSON obsługują \uxxxxskładnię Unicode, dzięki czemu można json_decodepracować nad sztucznie utworzoną reprezentacją ciągu JSON. Zmieniłem jednak sformułowanie, aby to wyjaśnić.
Stefan Gehrig
3
Ok, więc ścisłe sformułowanie jednej odpowiedzi na moje pytanie to: $ str = json_decode ('"\ u1000"'); Dziękuję Ci.
Telaclavo
Próbowałem echo json_decode('\u201B');Który odnosi się do pojedynczego cofniętego cytatu Jednak to nie działa, co oznacza brak wyjścia (nawet jeśli jest przesyłane do hd)
hek2mgl
4
Potrzebujesz echo json_decode('"\u201B"');. Podwójne cudzysłowy wokół symbolu Unicode są obowiązkowe.
Stefan Gehrig
162

W PHP 7.0.0 wprowadzono składnię „ucieczki punktu kodowego Unicode” .

Teraz można łatwo pisać znaki Unicode, używając podwójnego cudzysłowu lub ciągu heredoc , bez wywoływania żadnej funkcji.

$unicodeChar = "\u{1000}";
Czarna dziura
źródło
Można to wykorzystać w następujący sposób: wordwrap($longLongText, 20, "\u{200B}", true);( jest to przestrzeń o zerowej szerokości )
sanmai
5
Myślę, że OP chciał tej odpowiedzi, a nie zaakceptowanej odpowiedzi. W każdym razie, kiedy szukałem „Unicode w PHP”, to dlatego, że chciałem otrzymać tę odpowiedź, a nie zaakceptowaną. Może „\ u {abcd}” nie istniało, gdy zadawano to pytanie po raz pierwszy. Jeśli tak, to zaakceptowaną odpowiedź należy teraz przesunąć.
Adam Chalcraft
23

Zastanawiam się, dlaczego nikt jeszcze o tym nie wspomniał, ale możesz zrobić prawie równoważną wersję, używając sekwencji ucieczki w podwójnych cudzysłowach :

\x[0-9A-Fa-f]{1,2}

Sekwencja znaków pasujących do wyrażenia regularnego to znak w notacji szesnastkowej.

Przykład ASCII:

<?php
    echo("\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64\x21");
?>

Witaj świecie!

Więc w twoim przypadku wszystko, co musisz zrobić, to $str = "\x30\xA2";. Ale to są bajty , a nie znaki. Bajtowa reprezentacja punktu kodowego Unicode pokrywa się z big endianem UTF-16, więc możemy wydrukować go bezpośrednio jako taki:

<?php
    header('content-type:text/html;charset=utf-16be');
    echo("\x30\xA2");
?>

Jeśli używasz innego kodowania, będziesz musiał odpowiednio zmienić bajty (głównie za pomocą biblioteki, choć jest to również możliwe ręcznie).

Przykład Little Endian UTF-16:

<?php
    header('content-type:text/html;charset=utf-16le');
    echo("\xA2\x30");
?>

Przykład UTF-8:

<?php
    header('content-type:text/html;charset=utf-8');
    echo("\xE3\x82\xA2");
?>

Jest też packfunkcja, ale można się spodziewać, że będzie działać wolno.

Pacerier
źródło
Idealny do kopiowania / wklejania znaku punktora (\ xE2 \ x80 \ xA2) może spowodować błąd kodowania UTF-8 w dokumencie źródłowym. Dziękuję Ci.
jimp
21

PHP nie zna tych sekwencji ucieczki Unicode. Ale ponieważ nieznane sekwencje specjalne pozostają nienaruszone, możesz napisać własną funkcję, która konwertuje takie sekwencje specjalne Unicode:

function unicodeString($str, $encoding=null) {
    if (is_null($encoding)) $encoding = ini_get('mbstring.internal_encoding');
    return preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/u', create_function('$match', 'return mb_convert_encoding(pack("H*", $match[1]), '.var_export($encoding, true).', "UTF-16BE");'), $str);
}

Lub z anonimowym wyrażeniem funkcyjnym zamiast create_function:

function unicodeString($str, $encoding=null) {
    if (is_null($encoding)) $encoding = ini_get('mbstring.internal_encoding');
    return preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/u', function($match) use ($encoding) {
        return mb_convert_encoding(pack('H*', $match[1]), $encoding, 'UTF-16BE');
    }, $str);
}

Jego użycie:

$str = unicodeString("\u1000");
Gumbo
źródło
10
html_entity_decode('&#x30a8;', 0, 'UTF-8');

To też działa. Jednak rozwiązanie json_decode () jest dużo szybsze (około 50 razy).

flori
źródło
Prosta, elegancka, prosta i całkowicie bezpieczna metoda. +10
andreszs
7

Wypróbuj Portable UTF-8 :

$str = utf8_chr( 0x1000 );
$str = utf8_chr( '\u1000' );
$str = utf8_chr( 4096 );

Wszystkie działają dokładnie tak samo. Możesz uzyskać kod znaku za pomocą utf8_ord(). Przeczytaj więcej o Portable UTF-8 .

Hamid Sarfraz
źródło
3

Jak wspominali inni, PHP 7 bezpośrednio wprowadza obsługę \uskładni Unicode.

Jak również wspominali inni, jedynym sposobem na uzyskanie wartości ciągu z dowolnego sensownego opisu znaku Unicode w PHP jest przekonwertowanie go z czegoś innego (np. Parsowanie JSON, parsowanie HTML lub inna forma). Ale wiąże się to z kosztem wydajności w czasie wykonywania.

Jest jednak jeszcze jedna opcja. Możesz kodować znak bezpośrednio w PHP za pomocą \xbinarnych znaków ucieczki. \xSkładnia ucieczka jest również obsługiwana w PHP 5 .

Jest to szczególnie przydatne, jeśli nie chcesz wprowadzać znaku bezpośrednio w ciągu w jego naturalnej formie. Na przykład, jeśli jest to niewidoczny znak sterujący lub inny trudny do wykrycia biały znak.

Najpierw przykład dowodu:

// Unicode Character 'HAIR SPACE' (U+200A)
$htmlEntityChar = "&#8202;";
$realChar = html_entity_decode($htmlEntityChar);
$phpChar = "\xE2\x80\x8A";
echo 'Proof: ';
var_dump($realChar === $phpChar); // bool(true)

Zauważ, że jak wspomniał Pacerier w innej odpowiedzi, ten kod binarny jest unikalny dla określonego kodowania znaków. W powyższym przykładzie \xE2\x80\x8Ajest kodowanie binarne dla U + 200A w UTF-8.

Następne pytanie brzmi: jak dostać się z U+200Ado \xE2\x80\x8A?

Poniżej znajduje się skrypt PHP do generowania sekwencji ucieczki dla dowolnego znaku na podstawie ciągu JSON, encji HTML lub dowolnej innej metody, gdy masz ją jako natywny ciąg.

function str_encode_utf8binary($str) {
    /** @author Krinkle 2018 */
    $output = '';
    foreach (str_split($str) as $octet) {
        $ordInt = ord($octet);
        // Convert from int (base 10) to hex (base 16), for PHP \x syntax
        $ordHex = base_convert($ordInt, 10, 16);
        $output .= '\x' . $ordHex;
    }
    return $output;
}

function str_convert_html_to_utf8binary($str) {
    return str_encode_utf8binary(html_entity_decode($str));
}
function str_convert_json_to_utf8binary($str) {
    return str_encode_utf8binary(json_decode($str));
}

// Example for raw string: Unicode Character 'INFINITY' (U+221E)
echo str_encode_utf8binary('∞') . "\n";
// \xe2\x88\x9e

// Example for HTML: Unicode Character 'HAIR SPACE' (U+200A)
echo str_convert_html_to_utf8binary('&#8202;') . "\n";
// \xe2\x80\x8a

// Example for JSON: Unicode Character 'HAIR SPACE' (U+200A)
echo str_convert_json_to_utf8binary('"\u200a"') . "\n";
// \xe2\x80\x8a
Timo Tijhof
źródło
0
function unicode_to_textstring($str){

    $rawstr = pack('H*', $str);

    $newstr =  iconv('UTF-16BE', 'UTF-8', $rawstr);
    return $newstr;
}

$ msg = '67714eac99c500200054006f006b0079006f002000530074006100740069006f006e003a0020';

echo unicode_to_textstring ($ str);

Chochołowska 228
źródło