Как получить номер кодовой точки для заданного символа в строке utf-8?
Я хочу получить кодовые точки UCS-2 для заданной строки UTF-8. Например, слово "привет" должно выглядеть примерно так: "0068 0065 006C 006C 006F". Обратите внимание, что символы могут быть из любого языка, включая сложные сценарии, такие как восточно-азиатские языки.
Итак, проблема сводится к тому, чтобы "преобразовать данный символ в его кодовую точку UCS-2"
Но как? Пожалуйста, любая помощь будет очень признательна, так как я очень спешу.
Транскрипция ответа опрашивающего, размещенного как ответ
Спасибо за ваш ответ, но это нужно сделать в PHP v 4 или 5, а не 6.
Строка будет введена пользователем из поля формы.
Я хочу реализовать PHP-версию utf8to16 или utf8decode, например
function get_ucs2_codepoint($char)
{
// calculation of ucs2 codepoint value and assign it to $hex_codepoint
return $hex_codepoint;
}
Можете ли вы помочь мне с PHP или это можно сделать с помощью PHP с версией, упомянутой выше?
4 ответа
Scott Reynen написал функцию для преобразования UTF-8 в Unicode. Я нашел это, глядя на документацию PHP.
function utf8_to_unicode( $str ) {
$unicode = array();
$values = array();
$lookingFor = 1;
for ($i = 0; $i < strlen( $str ); $i++ ) {
$thisValue = ord( $str[ $i ] );
if ( $thisValue < ord('A') ) {
// exclude 0-9
if ($thisValue >= ord('0') && $thisValue <= ord('9')) {
// number
$unicode[] = chr($thisValue);
}
else {
$unicode[] = '%'.dechex($thisValue);
}
} else {
if ( $thisValue < 128)
$unicode[] = $str[ $i ];
else {
if ( count( $values ) == 0 ) $lookingFor = ( $thisValue < 224 ) ? 2 : 3;
$values[] = $thisValue;
if ( count( $values ) == $lookingFor ) {
$number = ( $lookingFor == 3 ) ?
( ( $values[0] % 16 ) * 4096 ) + ( ( $values[1] % 64 ) * 64 ) + ( $values[2] % 64 ):
( ( $values[0] % 32 ) * 64 ) + ( $values[1] % 64 );
$number = dechex($number);
$unicode[] = (strlen($number)==3)?"%u0".$number:"%u".$number;
$values = array();
$lookingFor = 1;
} // if
} // if
}
} // for
return implode("",$unicode);
} // utf8_to_unicode
Используйте существующую утилиту, такую как iconv, или любые другие библиотеки, поставляемые с языком, который вы используете.
Если вы настаиваете на развертывании собственного решения, ознакомьтесь с форматом UTF-8. По сути, каждая кодовая точка хранится как 1-4 байта, в зависимости от значения кодовой точки. Диапазоны следующие:
- U + 0000 - U + 007F: 1 байт: 0xxxxxxx
- U + 0080 - U + 07FF: 2 байта: 110xxxxx 10xxxxxx
- U + 0800 - U + FFFF: 3 байта: 1110xxxx 10xxxxxx 10xxxxxx
- U + 10000 - U + 10FFFF: 4 байта: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
Где каждый х - бит данных. Таким образом, вы можете определить, сколько байтов составляет каждую кодовую точку, посмотрев на первый байт: если он начинается с 0, это 1-байтовый символ. Если он начинается с 110, это 2-байтовый символ. Если он начинается с 1110, это 3-байтовый символ. Если он начинается с 11110, это 4-байтовый символ. Если он начинается с 10, это не начальный байт многобайтового символа. Если он начинается с 11111, это недопустимый символ.
После того, как вы выясните, сколько байтов в символе, это просто вопрос, если немного. Также обратите внимание, что UCS-2 не может представлять символы выше U+FFFF.
Поскольку вы не указали язык, вот несколько примеров кода C (проверка ошибок не включена):
wchar_t utf8_char_to_ucs2(const unsigned char *utf8)
{
if(!(utf8[0] & 0x80)) // 0xxxxxxx
return (wchar_t)utf8[0];
else if((utf8[0] & 0xE0) == 0xC0) // 110xxxxx
return (wchar_t)(((utf8[0] & 0x1F) << 6) | (utf8[1] & 0x3F));
else if((utf8[0] & 0xF0) == 0xE0) // 1110xxxx
return (wchar_t)(((utf8[0] & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6) | (utf8[2] & 0x3F));
else
return ERROR; // uh-oh, UCS-2 can't handle code points this high
}
PHP-код (который предполагает допустимый utf-8, без проверки на недействительный utf-8):
function ord_utf8($c) {
$b0 = ord($c[0]);
if ( $b0 < 0x10 ) {
return $b0;
}
$b1 = ord($c[1]);
if ( $b0 < 0xE0 ) {
return (($b0 & 0x1F) << 6) + ($b1 & 0x3F);
}
return (($b0 & 0x0F) << 12) + (($b1 & 0x3F) << 6) + (ord($c[2]) & 0x3F);
}
Я удивлен, потому что я только дал эту проблему студентам на заключительном экзамене. Вот эскиз UTF-8:
hex binary UTF-8 binary
0000-007F 00000000 0abcdefg => 0abcdefg
0080-07FF 00000abc defghijk => 110abcde 10fghijk
0800-FFFF abcdefgh ijklmnop => 1110abcd 10efghij 10klmnop
А вот код C99:
static void check(char c) {
if ((c & 0xc0) != 0xc0) RAISE(Bad_UTF8);
}
uint16_t Utf8_decode(char **p) { // return code point and advance *p
char *s = *p;
if ((s[0] & 0x80) == 0) {
(*p)++;
return s[0];
} else if ((s[0] & 0x40) == 0) {
RAISE (Bad_UTF8);
return ~0; // prevent compiler warning
} else if ((s[0] & 0x20) == 0) {
if ((s[0] & 0xf0) != 0xe0) RAISE (Bad_UTF8);
check(s[1]); check(s[2]);
(*p) += 3;
return ((s[0] & 0x0f) << 12)
+ ((s[1] & 0x3f) << 6)
+ ((s[2] & 0x3f));
} else {
check(s[1]);
(*p) += 2;
return ((s[0] & 0x1f) << 6)
+ ((s[1] & 0x3f));
}
}
Использовать mb_ord()
в php >= 7.2
.
Или эта функция:
function ord_utf8($c) {
$len = strlen($c);
$code = ord($c);
if($len > 1) {
$code &= 0x7F >> $len;
for($i = 1; $i < $len; $i++) {
$code <<= 6;
$code += ord($c[$i]) & 0x3F;
}
}
return $code;
}
$c
это персонаж. Если вам нужно преобразовать строку в массив символов, вы можете использовать это.
$string = 'abcde';
$string = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);