Как преобразовать кодовую точку Unicode / UCS в суррогатную пару UTF16?

Как преобразовать туда и обратно между кодовой точкой Unicode / UCS и суррогатной парой UTF16 в C ++ 14 и позже?

РЕДАКТИРОВАТЬ: Убрано упоминание о суррогатах UCS-2, так как такой вещи нет. Спасибо @ Реми-Лебо!

-1

Решение

В C ++ 11 и более поздних версиях вы можете использовать std::wstring_convert для преобразования между различными кодировками UTF / UCS, используя следующие std::codecvt типы:

  • UTF-8, <-> UCS-2:
    std::codecvt_utf8<char16_t>

  • UTF-8, <-> UTF-16:
    std::codecvt_utf8_utf16

  • UTF-8, <-> UTF-32 / UCS-4:
    std::codecvt_utf8<char32_t>

  • UCS-2 <-> UTF-16:
    std::codecvt_utf16<char16_t>

  • UTF-16 <-> UTF-32 / UCS-4:
    std::codecvt_utf16<char32_t>

  • UCS-2 <-> UTF-32 / UCS-4:
    нет стандартного преобразования, но вы можете написать свой собственный std::codecvt класс для него, если это необходимо. В противном случае используйте одно из приведенных выше преобразований между:
    UCS-2 <-> UTF-X <-> UTF-32/UCS-4

Вам не нужно обрабатывать суррогаты вручную.

Ты можешь использовать std::u32string держать ваш код (ы), и std::u16string для хранения ваших кодовых блоков UTF-16 / UCS-2.

Например:

using convert_utf16_uf32 = std::wstring_convert<std::codecvt_utf16<char32_t>, char16_t>;

std::u16string CodepointToUTF16(const char32_t codepoint)
{
const char32_t *p = &codepoint;
return convert_utf16_uf32{}.from_bytes(
reinterpret_cast<const char*>(p),
reinterpret_cast<const char*>(p+1)
);
}

std::u16string UTF32toUTF16(const std::u32string &str)
{
return convert_utf16_uf32{}.from_bytes(
reinterpret_cast<const char*>(str.data()),
reinterpret_cast<const char*>(str.data()+str.size())
);
}

char32_t UTF16toCodepoint(const std::u16string &str)
{
std::string bytes = convert_utf16_uf32{}.to_bytes(str);
return *(reinterpret_cast<const char32_t*>(bytes.data()));
}

std::u32string UTF16toUTF32(const std::u16string &str)
{
std::string bytes = convert_utf16_uf32{}.to_bytes(str);
return std::u32string(
reinterpret_cast<const char32_t*>(bytes.data()),
bytes.size() / sizeof(char32_t)
);
}
4

Другие решения

тег информационная страница объясняет (лучше, чем указано в Юникод Стандарт 9.0 в п. 3.9, таблица 3-5.) алгоритм преобразования из кодовой точки в суррогатную пару следующим образом:

Символы Unicode вне базовой многоязычной плоскости, то есть символы с кодом выше 0xFFFF, кодируются в UTF-16 парами 16-битных кодовых единиц, называемых суррогатными парами, по следующей схеме:

  • 0x010000 вычитается из кодовой точки, оставляя 20-битное число в диапазоне 0..0x0FFFFF;
  • первые десять битов (число в диапазоне 0..0x03FF) добавляются к 0xD800 для получения первой единицы кода или старшего суррогата, который будет в диапазоне 0xD800..0xDBFF;
  • младшие десять битов (также в диапазоне 0..0x03FF) добавляются к 0xDC00, чтобы получить вторую кодовую единицу или младший заменитель, который будет в диапазоне 0xDC00..0xDFFF.

В C ++ 14 и более поздних версиях это можно записать так:

#include <cstdint>

using codepoint = std::uint32_t;
using utf16 = std::uint16_t;

struct surrogate {
utf16 high; // Leading
utf16 low;  // Trailing
};

constexpr surrogate split(codepoint const in) noexcept {
auto const inMinus0x10000 = (in - 0x10000);
surrogate const r{
static_cast<utf16>((inMinus0x10000 / 0x400) + 0xd800), // High
static_cast<utf16>((inMinus0x10000 % 0x400) + 0xdc00)}; // Low
return r;
}

В обратном направлении нужно просто объединить последние 10 битов из старшего суррогата и последние 10 битов из младшего суррогата и добавить 0x10000:

constexpr codepoint combine(surrogate const s) noexcept {
return static_cast<codepoint>(
((s.high - 0xd800) * 0x400) + (s.low - 0xdc00) + 0x10000);
}

Вот тест для этих конверсий:

#include <cassert>

constexpr bool isValidUtf16Surrogate(utf16 v) noexcept
{ return (v & 0xf800) == 0xd800; }

constexpr bool isValidCodePoint(codepoint v) noexcept {
return (v <= 0x10ffff)
&& ((v >= 0x10000) || !isValidUtf16Surrogate(static_cast<utf16>(v)));
}

constexpr bool isValidUtf16HighSurrogate(utf16 v) noexcept
{ return (v & 0xfc00) == 0xd800; }

constexpr bool isValidUtf16LowSurrogate(utf16 v) noexcept
{ return (v & 0xfc00) == 0xdc00; }

constexpr bool codePointNeedsUtf16Surrogates(codepoint v) noexcept
{ return (v >= 0x10000) && (v <= 0x10ffff); }

void test(codepoint const in) {
assert(isValidCodePoint(in));
assert(codePointNeedsUtf16Surrogates(in));
auto const s = split(in);
assert(isValidUtf16HighSurrogate(s.high));
assert(isValidUtf16LowSurrogate(s.low));
auto const out = combine(s);
assert(isValidCodePoint(out));
assert(in == out);
}

int main() {
for (codepoint c = 0x10000; c <= 0x10ffff; ++c)
test(c);
}
4

По вопросам рекламы [email protected]