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