UTF-16サロゲートペア判定とUTF-16/UTF-32文字列の相互変換コードです。Unicodeの仕様はネット上の解説ページを参照しているため、間違えてる可能性もあります。
#include <algorithm> #include <string> namespace u16_surrogatepair { 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 = 0x010000; const char32_t codepoint_surrogatepair_last = 0x10ffff; // 文字が上位サロゲートであれば真を返します。 constexpr bool is_high_surrogate(char16_t c) { return high_surrogate_first <= c && c <= high_surrogate_last; } // 文字が下位サロゲートであれば真を返します。 constexpr bool is_low_surrogate(char16_t c) { return low_surrogate_first <= c && c <= low_surrogate_last; } // 2つの文字がサロゲートペアであれば真を返します。 constexpr bool is_surrogatepair(char16_t high, char16_t low) { return is_high_surrogate(high) && is_low_surrogate(low); } // 2つの文字がサロゲートペアであればコードポイントへ変換して真を返します。 // サロゲートペアでない場合は偽を返します。 constexpr bool surrogatepair_to_codepoint(char16_t high, char16_t low, char32_t& codepoint) { 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) { return codepoint_surrogatepair_first <= codepoint && codepoint <= codepoint_surrogatepair_last; } // コードポイントがサロゲートペアに対応する場合は変換して真を返します。 // サロゲートペアに対応しない場合は偽を返します。 constexpr bool codepoint_to_surrogatepair(char32_t codepoint, char16_t& high, char16_t& low) { if (!is_codepoint_surrogatepair(codepoint)) { return false; } auto c = codepoint - codepoint_surrogatepair_first; high = (c >> 10) | high_surrogate_first; low = (c & 0b1111111111) | low_surrogate_first; return true; } } // UTF-16文字列をUTF-32文字列へ変換します。 // 変換が成功した場合は真を返します。 bool to_u32string(const std::u16string& s, std::u32string& result) { result.clear(); result.reserve(s.size() / 2); for (size_t i = 0, l = s.size(); i < l; i++) { auto c1 = s[i]; if (u16_surrogatepair::is_high_surrogate(c1)) { if (++i == l) return false; auto c2 = s[i]; char32_t codepoint; if (!u16_surrogatepair::surrogatepair_to_codepoint(c1, c2, codepoint)) return false; result.push_back(codepoint); } else { result.push_back(c1); } } return true; } // UTF-16文字列をUTF-32文字列へ変換します。 // 変換は常に成功するため、常に真を返します。 bool to_u16string(const std::u32string& s, std::u16string& result) { result.clear(); result.reserve(s.size() * 2); for (size_t i = 0, l = s.size(); i < l; i++) { auto c = s[i]; if (u16_surrogatepair::is_codepoint_surrogatepair(c)) { char16_t high, low; u16_surrogatepair::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; } int main() { std::u32string s32; to_u32string(u"🍊🍎😁", s32); std::u16string s16; to_u16string(s32, s16); }