如何在 Unicode/UCS 代码点和 UTF16 代理项对之间进行转换?
How to convert between a Unicode/UCS codepoint and a UTF16 surrogate pair?
如何在 C++14 及更高版本中的 Unicode/UCS 代码点和 UTF16 代理项对之间来回转换?
编辑: 删除了对 UCS-2 代理项的提及,因为没有这样的东西。谢谢@remy-lebeau!
§3.9中的surrogate-pairs tag info page explains (better than specified by the Unicode Standard 9.0,Table3-5.)从codepoint到surrogate pair的转换算法如下:
Unicode characters outside the Basic Multilingual Plane, that is characters with code above 0xFFFF, are encoded in UTF-16 by pairs of 16-bit code units called surrogate pairs, by the following scheme:
- 0x010000 is subtracted from the code point, leaving a 20-bit number in the range 0..0x0FFFFF;
- the top ten bits (a number in the range 0..0x03FF) are added to 0xD800 to give the first code unit or high surrogate, which will be in the range 0xD800..0xDBFF;
- the low ten bits (also in the range 0..0x03FF) are added to 0xDC00 to give the second code unit or low surrogate, which will be in the range 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);
}
在 C++11 及更高版本中,您可以使用 std::wstring_convert
to convert between various UTF/UCS encodings, using the following 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
class。否则,在两者之间使用上述转换之一:
UCS-2 <-> UTF-X <-> UTF-32/UCS-4
您不需要手动处理代理人。
您可以使用 std::u32string
to hold your codepoint(s), and 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)
);
}
如何在 C++14 及更高版本中的 Unicode/UCS 代码点和 UTF16 代理项对之间来回转换?
编辑: 删除了对 UCS-2 代理项的提及,因为没有这样的东西。谢谢@remy-lebeau!
§3.9中的surrogate-pairs tag info page explains (better than specified by the Unicode Standard 9.0,Table3-5.)从codepoint到surrogate pair的转换算法如下:
Unicode characters outside the Basic Multilingual Plane, that is characters with code above 0xFFFF, are encoded in UTF-16 by pairs of 16-bit code units called surrogate pairs, by the following scheme:
- 0x010000 is subtracted from the code point, leaving a 20-bit number in the range 0..0x0FFFFF;
- the top ten bits (a number in the range 0..0x03FF) are added to 0xD800 to give the first code unit or high surrogate, which will be in the range 0xD800..0xDBFF;
- the low ten bits (also in the range 0..0x03FF) are added to 0xDC00 to give the second code unit or low surrogate, which will be in the range 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);
}
在 C++11 及更高版本中,您可以使用 std::wstring_convert
to convert between various UTF/UCS encodings, using the following 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
class。否则,在两者之间使用上述转换之一:
UCS-2 <-> UTF-X <-> UTF-32/UCS-4
您不需要手动处理代理人。
您可以使用 std::u32string
to hold your codepoint(s), and 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)
);
}