今回は、Unicodeの1文字(正確にはコードポイント)を読み書きする関数、そしてUTF-16の文字列の長さを求める関数を定義します。今回も同様、C++11以降かつint型が32ビットの処理系を仮定しています。
まずは関数の定義に必要になる定数を定義します。
0 1 2 3 4 5 |
// UTF-16の最大値 constexpr char16_t utf16_max = 0xffff; // Unicodeの最大値 constexpr char32_t unicode_max = 0x10ffff; |
それではこれらの定数を使って、それぞれの関数を定義していきます。
Unicodeの1文字をUTF-16の列として書き込む
Unicodeの1文字をOutputIteratorに書き込みます。本来であれば、OutputIteratorのvalue_typeはchar16_tでなければならないのですが、unsigned short型やwchar_t型などでも使えるようにチェックしていません。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
template <typename OutputIterator> OutputIterator utf16_putchar(char32_t c, OutputIterator first, OutputIterator last) { if (first == last) throw std::out_of_range(__func__); if (c <= utf16_max) { *first++ = static_cast<char16_t>(c); } else if (c <= unicode_max) { *first++ = static_cast<char16_t>((c - 0x10000) / 0x400 + 0xd800); if (first == last) throw std::out_of_range(__func__); *first++ = static_cast<char16_t>((c - 0x10000) % 0x400 + 0xdc00); } else { throw std::invalid_argument(__func__); } return first; } |
UTF-16の列からUnicodeの1文字を読み込む
次は先ほどの逆で、InputIteratorからUnicodeの1文字を読み込みます。先ほど同様、あえてInputIteratorのvalue_typeはチェックしていません。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
template <typename InputIterator> char32_t utf16_getchar(InputIterator& next, InputIterator last) { if (next == last) throw std::invalid_argument(__func__); char32_t c = *next++; auto h = c; if (is_high_surrogate(h)) { if (next == last) throw std::invalid_argument(__func__); auto l = *next++; if (!is_low_surrogate(l)) throw std::invalid_argument(__func__); c = 0x10000 + (h - 0xd800) * 0x400 + (l - 0xdc00); if (c <= utf16_max || unicode_max < c) throw std::invalid_argument(__func__); } return c; } |
UTF-16文字列の長さを求める
最後にUTF-16文字列の長さ(コードポイント数)を求める関数を定義します。サロゲートペアの片側しかない場合も1コードポイントとして数えています。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
template <typename InputIterator> std::size_t utf16_length(InputIterator& next, InputIterator last) { std::size_t r = 0; char16_t h = 0; while (next != last) { char16_t c = *next++; ++r; if (is_high_surrogate(c)) { if (next != last && is_low_surrogate(*next)) ++next; } } return r; } |