Когда вы читаете, как через fread, текстовый файл, закодированный как UTF-8, как вы можете определить, сколько байтов займет символ? - PullRequest
0 голосов
/ 08 ноября 2019

Если мы хотим представить больше символов, чем позволяет ASCII, мы можем использовать Unicode, который использует больше битов, чем ASCII, для представления некоторых символов. Одна реализация Unicode, UTF-8, использует «кодирование переменной ширины» для представления символов: символы могут быть представлены одним, двумя, тремя или четырьмя байтами.

1 Ответ

1 голос
/ 12 ноября 2019

Как вы говорите, кодовая точка Unicode (то, что вы называете символом) может быть представлена ​​в UTF-8 с использованием 1,4 кодовых единиц (8-битных байтов) каждая. Битовая комбинация первой кодовой единицы сообщает вам, сколько кодовых единиц используется:

image

Кодовые точки U + 0000..U + 007F используют 1 кодединица, где единичный кодовый блок имеет свой старший бит, установленный на 0.

Кодовые точки U + 0080..U + 07FF используют 2 кодовые единицы, где 1-й кодовой единице имеет свои старшие 3 бита, установленные на 110.

Кодовые точки U + 0800..U + FFFF используют 3 кодовые единицы, где для 1-й кодовой единицы старшие 4 бита установлены в 1110.

Кодовые точки U + 10000. .U + 10FFFF используют 4 кодовые единицы, где 1-я кодовая единица имеет свои старшие 5 битов, установленные на 11110.

Учитывая 1-ую кодовую единицу кодированной точки UTF-8, вы можете маскировать ее биты с помощьюлогический оператор AND для определения используемого шаблона, например:

int32_t readUTF8Char(FILE *f)
{
    uint8_t b;

    if (fread(&b, 1, 1, f) != 1) {
        // read error
        return -1;
    }

    if ((b & 0x80) == 0)
    {
        // 1 byte, use b as-is ...
        return b;
    }

    int32_t codePoint;
    int num = 0;

    if ((b & 0xE0) == 0xC0) {
        // 2 bytes, read 1 more byte ...
        codePoint = b & 0x1F;
        num = 1;
    }
    else if ((b & 0xF0) == 0xE0) {
        // 3 bytes, read 2 more bytes ...
        codePoint = b & 0x0F;
        num = 2;
    }
    else if ((b & 0xF8) == 0xF0) {
        // 4 bytes, read 3 more bytes ...
        codePoint = b & 0x07;
        num = 3;
    }
    else {
        // malformed...
        return -1;
    }

    for(int i = 0; i < num; ++i) {
        if (fread(&b, 1, 1, f) != 1) {
            // read error
            return -1;
        }
        if ((b & 0xC0) != 0x80) {
            // malformed
            return -1;
        }
        codePoint = (codePoint << 6) | (b & 0x3F);
    }
    return codePoint;
}
...