Как преобразовать 4 байта UTF-8 в код Unicode?
F0 9F 98 81 -> 1F601
Альтернативный вариант — воспользоваться встроенным классом Encoding. Encoding.UTF8.GetString даёт нужную строку, но если перебирать её посимвольно, то мы получим слова UTF-16. Это обычно то, что нужно, кроме случая суррогатной пары. Это именно ваш случай.
Поэтому напишем процедуру, которая будет сканировать строку по символу и объединять суррогатные пары в одно значение.
static void Main(string[] args)
{
var bytes = new byte[] { 0xF0, 0x9F, 0x98, 0x81 };
var s = Encoding.UTF8.GetString(bytes);
foreach (var v in GetCodes(s))
Console.Write($"{v:X} ");
}
static IEnumerable<int> GetCodes(string s)
{
char? high = null;
foreach (var c in s)
{
if (high == null)
{
if (char.IsHighSurrogate(c))
high = c;
else if (char.IsLowSurrogate(c))
throw new ArgumentException("Unpaired low surrogate");
else
yield return c;
}
else
{
if (char.IsLowSurrogate(c))
yield return char.ConvertToUtf32(high.Value, c);
else
throw new ArgumentException("Unpaired high surrogate");
high = null;
}
}
if (high != null)
throw new ArgumentException("Unpaired high surrogate");
}
Очевидный альтернативный подход — сконвертировать строку в UTF32, там-то уж никаких специальных случаев наподобие суррогатных пар нет. (UTF8, можно сказать, весь состоит из этих самых специальных случаев.)
Другие варианты решения обсуждаются здесь (верхний ответ, как пишет сам автор, неправилен).
Стандартного такого перевода я не нашел, поэтому написал свой.
Предположим, что эти байты сохранены в массив:
byte[] utf8Bytes = { 0xF0, 0x9F, 0x98, 0x81 };
Если в массиве всегда хранится ровно один символ (то есть можно опираться на длину массива), то метод перевода будет выглядеть так:
public static int GetUnicode(byte[] utf8Bytes)
{
byte firstByteMask = 0x7F;
if (utf8Bytes.Length > 1)
{
firstByteMask >>= utf8Bytes.Length;
}
int result = utf8Bytes[0] & firstByteMask;
for (int i = 1; i < utf8Bytes.Length; i++)
{
result <<= 6;
result += utf8Bytes[i] & 0x3F;
}
return result;
}
Если же количество байтов в коде символа нужно рассчитывать на основании значения первого байта, то код будет длиннее:
public static int GetUnicode(byte[] utf8Bytes)
{
byte firstByte = utf8Bytes[0];
int additionalBytesCount = 0;
byte firstByteMask = 0x7F;
if ((firstByte & 0x80) != 0)
{
for (byte i = 0x40; (firstByte & i) == i; i >>= 1)
{
additionalBytesCount++;
}
firstByteMask >>= additionalBytesCount + 1;
}
int result = firstByte & firstByteMask;
for (int i = 1; i <= additionalBytesCount; i++)
{
result <<= 6;
result += utf8Bytes[i] & 0x3F;
}
return result;
}
Тест этого кода
byte[] utf8Bytes = { 0xF0, 0x9F, 0x98, 0x81 };
int result = GetUnicode(utf8Bytes);
Console.WriteLine(result.ToString("X"));
выведет в консоль 1F601.
Вот функция, которая преобразует последовательность байт в UCS-32 код
(правда, это С, а не C#)
/*
Получает адрес памяти с байтами, закодированными в UTF-8.
Возвращает UCS из одного или нескольких байт в памяти кодированных в UTF-8.
Во втором аргументе возвращает длину UTF-8 последовательности.
Третий аргумент индикатор ошибки. Если ошибки нет, то он устанавливается в 0.
При ошибке (недопустимая UTF-8 последовательность)
возвращает первый байт, второй параметр устанавливается в 1,
а третий задает смещение к байту, следующего за ошибочным.
*/
int
utf8_to_ucs (const char *utf, int *step, int *err)
{
if (step)
*step = 1;
if (err)
*err = 0;
u_int ucs = *utf & 0xFF, estep = 1;
int k, n = 5, efl = 0;
if (ucs > 127) {
// для любой длины utf-8
// FF, FE, 10xx xxxx в первом байте - error (not utf-8 !!!). Return it
if ((ucs & 0xC0) == 0x80 || ucs == 0xFF || ucs == 0xFE)
efl = 1;
else {
// 1111110x
while (n && (ucs & (1<<n)))
n--;
k = 7-n;
u_int uc = ucs & mask[n-1];
n = 6-n;
while (n--) {
estep++;
if ((*(++utf) & 0xC0) != 0x80) {
efl = 1;
break;
}
uc <<= 6;
uc |= (*utf & 0x3f);
}
if (!efl) {
if (step)
*step = k;
ucs = uc;
}
}
}
if (efl) {
if (err)
*err = estep;
errno = EILSEQ;
}
return ucs;
}
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости