C++標準ライブラリを使用してUTF-16とUTF-32を相互変換する関数のコードです。コンセプトにより制約するため、C++20以降用です。
u16_surrogate.hpp
#pragma once namespace u16_surrogate { const char16_t high_surrogate_first = 0xd800; const char16_t high_surrogate_last = 0xdbff; const char16_t low_surrogate_first = 0xdc00; const char16_t low_surrogate_last = 0xdfff; const char32_t codepoint_surrogatepair_first = 0x00010000; const char32_t codepoint_surrogatepair_last = 0x0010ffff; // 文字が上位サロゲートであれば真を返します。 constexpr bool is_high_surrogate(char16_t c) noexcept { return high_surrogate_first <= c && c <= high_surrogate_last; } // 文字が下位サロゲートであれば真を返します。 constexpr bool is_low_surrogate(char16_t c) noexcept { return low_surrogate_first <= c && c <= low_surrogate_last; } // 文字がサロゲートであれば真を返します。 constexpr bool is_surrogate(char16_t c) noexcept { return is_high_surrogate(c) || is_low_surrogate(c); } // 2つの文字がサロゲートペアであれば真を返します。 constexpr bool is_surrogatepair(char16_t high, char16_t low) noexcept { return is_high_surrogate(high) && is_low_surrogate(low); } // 2つの文字がサロゲートペアであればコードポイントへ変換して真を返します。 // サロゲートペアでない場合は偽を返します。 constexpr bool surrogatepair_to_codepoint(char16_t high, char16_t low, char32_t& codepoint) noexcept { if (!is_surrogatepair(high, low)) { return false; } codepoint = codepoint_surrogatepair_first + ((high - high_surrogate_first) << 10) | (low - low_surrogate_first); return true; } // コードポイントがサロゲートペアに対応する場合は真を返します。 constexpr bool is_codepoint_surrogatepair(char32_t codepoint) noexcept { return codepoint_surrogatepair_first <= codepoint && codepoint <= codepoint_surrogatepair_last; } // コードポイントがサロゲートペアに対応する場合は変換して真を返します。 // サロゲートペアに対応しない場合は偽を返します。 constexpr bool codepoint_to_surrogatepair(char32_t codepoint, char16_t& high, char16_t& low) noexcept { if (!is_codepoint_surrogatepair(codepoint)) { return false; } const auto c = codepoint - codepoint_surrogatepair_first; high = (c >> 10) | high_surrogate_first; low = (c & 0b1111111111) | low_surrogate_first; return true; } }
u16s_u32s.hpp
#pragma once #ifndef _STRING_ #error <string>のインクルードが必要です。 #endif #include "u16_surrogate.hpp" namespace u16s_u32s { template <typename result_type> requires requires (result_type result) { result.clear(); result.reserve(std::declval<size_t>()); result.push_back(std::declval<char32_t>()); } bool to_u32container(std::u16string_view source, result_type& result) noexcept( noexcept(result.clear()) && noexcept(result.reserve(std::declval<size_t>())) && noexcept(result.push_back(std::declval<char32_t>())) && noexcept(source[std::declval<size_t>()])) { const auto l = source.size(); result.clear(); result.reserve(l / 2); for (size_t i = 0; i < l; i++) { const auto c1 = source[i]; if (u16_surrogate::is_high_surrogate(c1)) { if (++i == l) return false; const auto c2 = source[i]; char32_t codepoint; if (!u16_surrogate::surrogatepair_to_codepoint(c1, c2, codepoint)) return false; result.push_back(codepoint); } else { result.push_back(c1); } } return true; } template <typename result_type> requires requires (result_type result) { result.clear(); result.reserve(std::declval<size_t>()); result.push_back(std::declval<char16_t>()); } bool to_u16container(std::u32string_view source, result_type& result) noexcept( noexcept(result.clear()) && noexcept(result.reserve(std::declval<size_t>())) && noexcept(result.push_back(std::declval<char16_t>())) && noexcept(source[std::declval<size_t>()])) { const auto l = source.size(); result.clear(); result.reserve(l * 2); for (size_t i = 0; i < l; i++) { const auto c = source[i]; if (u16_surrogate::is_codepoint_surrogatepair(c)) { char16_t high, low; u16_surrogate::codepoint_to_surrogatepair(c, high, low); result.push_back(high); result.push_back(low); } else { result.push_back(static_cast<char16_t>(c)); } } return true; } // UTF-16文字列をUTF-32文字列へ変換します。 // 変換が成功した場合は真を返します。 inline bool to_u32string(std::u16string_view source, std::u32string& result) noexcept(noexcept(to_u32container<std::u32string>(source, result))) { return to_u32container<std::u32string>(source, result); } // UTF-32文字列をUTF-16文字列へ変換します。 // 変換は常に成功するため、常に真を返します。 inline bool to_u16string(std::u32string_view source, std::u16string& result) noexcept(noexcept(to_u16container<std::u16string>(source, result))) { return to_u16container<std::u16string>(source, result); } #ifdef _VECTOR_ // UTF-16文字列をUTF-32文字ベクトルへ変換します。 // 変換が成功した場合は真を返します。 inline bool to_u32vector(std::u16string_view source, std::vector<char32_t>& result) noexcept(noexcept(to_u32container<std::vector<char32_t>>(source, result))) { return to_u32container<std::vector<char32_t>>(source, result); } // UTF-32文字列をUTF-16文字ベクトルへ変換します。 // 変換は常に成功するため、常に真を返します。 inline bool to_u16vector(std::u32string_view source, std::vector<char16_t>& result) noexcept(noexcept(to_u16container<std::vector<char16_t>>(source, result))) { return to_u16container<std::vector<char16_t>>(source, result); } #endif }
テストコード
#include <string> #include <vector> #include "u16s_u32s.hpp" int main() { std::u32string s32; u16s_u32s::to_u32string(u"🍊🍎😁", s32); std::u16string s16; u16s_u32s::to_u16string(s32, s16); std::vector<char32_t> v1; u16s_u32s::to_u32vector(u"🍊🍎😁", v1); }