Страницы

Поиск по вопросам

понедельник, 4 февраля 2019 г.

Количество байт, которое занимает UTF8 символ в char * строке?

Для итерации по словам строки в кодировке UTF8 мне понадобилось разобраться в том, как узнать по-первому байту символа общее количество его байт. Я только начинаю разбираться с UTF8, поэтому не совсем понимаю наверняка механику битовых операций, которая для этого используется (судя по большинству примеров в интернете).

На StackOverflow я нашёл два решения:
1) Способ 1
size_t utf8_char_length(char firstbyte) { if ((firstbyte & 0xC0) == 0xC0) { if ((firstbyte & 0xF0) == 0xF0) { return 4; } else if ((firstbyte & 0xE0) == 0xE0) { return 3; } else { return 2; } } else { return 1; } }
2) Второе решение найдено здесь, оно использует другой способ: вместо битовой операции & используется "lookup" по следующему массиву из 256 символов:
static const size_t utf8_skip_data[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 };

К сожалению, я сейчас не имею возможности досконально разобраться с UTF8, поэтому просто ищу надёжную функцию, которая будет давать мне правильные результаты для символов в кодировке в UTF8 (точнее для первых байт этих символов). Мне больше нравится второе решение, но я хочу услышать мнение более опытных специалистов:
какую функцию стоит использовать для такой задачи: 1, 2 или, может быть, вы знаете какую-то проверенную свою функцию, которая лучше, чем эти две?
P.S. Кстати, если кто-то может объяснить, откуда взялась lookup-таблица, буду признателен. На SO пишут, что она взята из исходного кода glib's gutf8.c. Так вот интересно, какой принцип лежит в её основе.
Спасибо.


Ответ

Вкратце алгоритм кодирования символов в UTF8 выглядит так: Если код символа меньше 128, то кодируется как есть. Если больше или равно, то кодируется в следующем формате: в первом байте в единичном коде записывается количество использованных байт включая первый (то есть записывается такое число единиц, сколько байт в числе; теоретически формат UTF8 не накладывает ограничений на количество байт, используемых для записи числа, но обычно используется не более 6). Затем записывается 0 (чтобы отделить количество байт числа от значащих разрядов числа), после чего идут биты, представляющие число. Каждый следующий байт начинается с последовательности 10 (чтобы не путать однобайтные и многобайтные символы - все однобайтные начинаются с нуля). Например, 111 0 0011 10 011101 10 000100 Сначала 3 единицы, значит, для записи используется 3 байта. Затем 0, отделяющий запись числа байт от записи числа Дальше значащие разряды числа. Первые 2 бита каждого байта (10) к ним не относятся - это метки, означающие, что это второй (третий, и т. д.) байт записи. Таким образом, записанный код - 0011011101000100. Это к вопросу о том, откуда взялась lookup-таблица. К вопросу какую функцию использовать - исходите из потребностей. Если вы всегда будете работать с символами, код которых не превышает 256, то используйте lookup-таблицу, она быстрее. Если же планируется полная поддержка Юникода, то используйте функцию. Только её придётся модифицировать, так как она не распознает 5- и 6-байтные символы. UPD Прошу прощения, выражение 'lookup-таблица' сбило меня с толку и навеяло неверные ассоциации. Да, действительно, эта таблица предназначена для проверки первого байта последовательности и решения о длине кода. Лично я всё равно склоняюсь к функции - она выглядит гораздо лаконичнее. Но lookup-таблица работает быстрее, если это принципиально, лучше использовать её.

Комментариев нет:

Отправить комментарий