Страницы

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

воскресенье, 5 января 2020 г.

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

#c #utf_8


Для итерации по словам строки в кодировке 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. Так вот интересно, какой
принцип лежит в её основе.

Спасибо.
    


Ответы

Ответ 1



Вкратце алгоритм кодирования символов в 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-таблица работает быстрее, если это принципиально, лучше использовать её.

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

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