Это все часть кодировки UTF8 (которая является единственной схемой кодирования для Unicode).
Размер можно выяснить, изучив первый байт следующим образом:
- если он начинается с битовой комбинации
"10" (0x80-0xbf)
, это не первый байт последовательности, и вы должны выполнять резервное копирование до тех пор, пока не найдете начало, любой байт, начинающийся с «0» или «11» (спасибо Джеффри Хантину за указывая на это в комментариях).
- если он начинается с битовой комбинации
"0" (0x00-0x7f)
, это 1 байт.
- если он начинается с битовой комбинации
"110" (0xc0-0xdf)
, это 2 байта.
- если он начинается с битовой комбинации
"1110" (0xe0-0xef)
, это 3 байта.
- если он начинается с битовой комбинации
"11110" (0xf0-0xf7)
, это 4 байта.
Я продублирую таблицу, показывающую это, но оригинал находится на странице Википедии UTF8 здесь .
+----------------+----------+----------+----------+----------+
| Unicode | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
+----------------+----------+----------+----------+----------+
| U+0000-007F | 0xxxxxxx | | | |
| U+0080-07FF | 110yyyxx | 10xxxxxx | | |
| U+0800-FFFF | 1110yyyy | 10yyyyxx | 10xxxxxx | |
| U+10000-10FFFF | 11110zzz | 10zzyyyy | 10yyyyxx | 10xxxxxx |
+----------------+----------+----------+----------+----------+
Символы Юникода в приведенной выше таблице составлены из битов:
000z-zzzz yyyy-yyyy xxxx-xxxx
где биты z
и y
предполагаются равными нулю, если они не заданы. Некоторые байты считаются недопустимыми в качестве начального байта, поскольку они либо:
- бесполезно: 2-байтовая последовательность, начинающаяся с 0xc0 или 0xc1, фактически дает кодовую точку, меньшую 0x80, которую можно лучше представить с помощью 1-байтовой последовательности.
- используется RFC3629 для 4-байтовой последовательности выше U + 10FFFF или 5-байтовой и 6-байтовой последовательностей. Это байты от 0xf5 до 0xfd.
- просто не используется: байты 0xfe и 0xff.
Кроме того, последующие байты в многобайтовой последовательности, которые не начинаются с битов "10", также недопустимы.
В качестве примера рассмотрим последовательность [0xf4,0x8a, 0xaf, 0x8d]. Это 4-байтовая последовательность, поскольку первый байт находится в диапазоне от 0xf0 до 0xf7.
0xf4 0x8a 0xaf 0x8d
= 11110100 10001010 10101111 10001101
zzz zzyyyy yyyyxx xxxxxx
= 1 0000 1010 1011 1100 1101
z zzzz yyyy yyyy xxxx xxxx
= U+10ABCD
Для вашего конкретного запроса с первым байтом 0xe6 (длина = 3) последовательность байтов:
0xe6 0xbe 0xb3
= 11100110 10111110 10110011
yyyy yyyyxx xxxxxx
= 01101111 10110011
yyyyyyyy xxxxxxxx
= U+6FB3
Если вы посмотрите этот код здесь , вы увидите, что он был в вашем вопросе: 澳.
Чтобы показать, как работает декодирование, я вернулся к своим архивам, чтобы найти код обработки UTF8. Мне пришлось немного изменить его, чтобы сделать его полноценной программой, и кодировка была удалена (поскольку вопрос был действительно о декодировании), поэтому я надеюсь, что я не внес никаких ошибок из вырезки и вставки:
#include <stdio.h>
#include <string.h>
#define UTF8ERR_TOOSHORT -1
#define UTF8ERR_BADSTART -2
#define UTF8ERR_BADSUBSQ -3
typedef unsigned char uchar;
static int getUtf8 (uchar *pBytes, int *pLen) {
if (*pLen < 1) return UTF8ERR_TOOSHORT;
/* 1-byte sequence */
if (pBytes[0] <= 0x7f) {
*pLen = 1;
return pBytes[0];
}
/* Subsequent byte marker */
if (pBytes[0] <= 0xbf) return UTF8ERR_BADSTART;
/* 2-byte sequence */
if ((pBytes[0] == 0xc0) || (pBytes[0] == 0xc1)) return UTF8ERR_BADSTART;
if (pBytes[0] <= 0xdf) {
if (*pLen < 2) return UTF8ERR_TOOSHORT;
if ((pBytes[1] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ;
*pLen = 2;
return ((int)(pBytes[0] & 0x1f) << 6)
| (pBytes[1] & 0x3f);
}
/* 3-byte sequence */
if (pBytes[0] <= 0xef) {
if (*pLen < 3) return UTF8ERR_TOOSHORT;
if ((pBytes[1] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ;
if ((pBytes[2] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ;
*pLen = 3;
return ((int)(pBytes[0] & 0x0f) << 12)
| ((int)(pBytes[1] & 0x3f) << 6)
| (pBytes[2] & 0x3f);
}
/* 4-byte sequence */
if (pBytes[0] <= 0xf4) {
if (*pLen < 4) return UTF8ERR_TOOSHORT;
if ((pBytes[1] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ;
if ((pBytes[2] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ;
if ((pBytes[3] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ;
*pLen = 4;
return ((int)(pBytes[0] & 0x0f) << 18)
| ((int)(pBytes[1] & 0x3f) << 12)
| ((int)(pBytes[2] & 0x3f) << 6)
| (pBytes[3] & 0x3f);
}
return UTF8ERR_BADSTART;
}
static uchar htoc (char *h) {
uchar u = 0;
while (*h != '\0') {
if ((*h >= '0') && (*h <= '9'))
u = ((u & 0x0f) << 4) + *h - '0';
else
if ((*h >= 'a') && (*h <= 'f'))
u = ((u & 0x0f) << 4) + *h + 10 - 'a';
else
return 0;
h++;
}
return u;
}
int main (int argCount, char *argVar[]) {
int i;
uchar utf8[4];
int len = argCount - 1;
if (len != 4) {
printf ("Usage: utf8 <hex1> <hex2> <hex3> <hex4>\n");
return 1;
}
printf ("Input: (%d) %s %s %s %s\n",
len, argVar[1], argVar[2], argVar[3], argVar[4]);
for (i = 0; i < 4; i++)
utf8[i] = htoc (argVar[i+1]);
printf (" Becomes: (%d) %02x %02x %02x %02x\n",
len, utf8[0], utf8[1], utf8[2], utf8[3]);
if ((i = getUtf8 (&(utf8[0]), &len)) < 0)
printf ("Error %d\n", i);
else
printf (" Finally: U+%x, with length of %d\n", i, len);
return 0;
}
Вы можете запустить его с вашей последовательностью байтов (вам понадобится 4, поэтому используйте 0 для их заполнения) следующим образом:
> utf8 f4 8a af 8d
Input: (4) f4 8a af 8d
Becomes: (4) f4 8a af 8d
Finally: U+10abcd, with length of 4
> utf8 e6 be b3 0
Input: (4) e6 be b3 0
Becomes: (4) e6 be b3 00
Finally: U+6fb3, with length of 3
> utf8 41 0 0 0
Input: (4) 41 0 0 0
Becomes: (4) 41 00 00 00
Finally: U+41, with length of 1
> utf8 87 0 0 0
Input: (4) 87 0 0 0
Becomes: (4) 87 00 00 00
Error -2
> utf8 f4 8a af ff
Input: (4) f4 8a af ff
Becomes: (4) f4 8a af ff
Error -3
> utf8 c4 80 0 0
Input: (4) c4 80 0 0
Becomes: (4) c4 80 00 00
Finally: U+100, with length of 2