Unicodeの相互変換
これも巷に溢れてるけど、Unicodeの変換ってどうなってるのかよく知らなかったから勉強がてらに書いてみた。
//#include <cstdint> namespace std { typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned long uint32_t; } #include <utility> #include <iterator> template<class InputIterator> std::uint8_t valid_utf8_read(InputIterator& it) { std::uint8_t c = *it++; if ((c & 0xc0) != 0x80) { // そんな文字コードしるかぼけー throw 0; } return c; } template<class InputIterator, class OutputIterator> std::pair<InputIterator, OutputIterator> utf8_to_utf32be(InputIterator it, OutputIterator out) { std::uint32_t c0 = *it++; if ((c0 & 0x80) == 0) { // U+0000〜U+007F *out++ = c0; } else if ((c0 & 0xE0) == 0xC0) { // U+0080〜U+07FF std::uint32_t c1 = valid_utf8_read(it); *out++ = ((c0 & 0x1F) << 6) | (c1 & 0x3F); } else if ((c0 & 0xF0) == 0xE0) { // U+0800〜U+FFFF std::uint32_t c1 = valid_utf8_read(it); std::uint32_t c2 = valid_utf8_read(it); *out++ = ((c0 & 0x0F) << 12) | ((c1 & 0x3F) << 6) | (c2 & 0x3F); } else if (0xF0 <= c0 && c0 <= 0xF4) { // U+10000〜U+10FFFF std::uint32_t c1 = valid_utf8_read(it); std::uint32_t c2 = valid_utf8_read(it); std::uint32_t c3 = valid_utf8_read(it); // 上位5bit c0 = ((c0 & 0x7) << 2) | ((c1 >> 4) & 0x3); if (c0 > 0x10) { // そんな文字コードしるかぼけー throw 1; } *out++ = (c0 << 16) | ((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); } else { // そんな文字コードしるかぼけー throw 2; } return std::make_pair(it, out); } template<class InputIterator, class OutputIterator> std::pair<InputIterator, OutputIterator> utf32be_to_utf8(InputIterator it, OutputIterator out) { std::uint32_t c0 = *it++; if ((c0 & ~0x007F) == 0) { // U+0000〜U+007F *out++ = static_cast<std::uint8_t>(c0); } else if ((c0 & ~0x07FF) == 0) { // U+0080〜U+07FF *out++ = static_cast<std::uint8_t>(0xC0 | (c0 >> 6)); *out++ = static_cast<std::uint8_t>(0x80 | (c0 & 0x3F)); } else if ((c0 & ~0xFFFF) == 0) { // U+0800〜U+FFFF *out++ = static_cast<std::uint8_t>(0xE0 | (c0 >> 12)); *out++ = static_cast<std::uint8_t>(0x80 | ((c0 >> 6) & 0x3F)); *out++ = static_cast<std::uint8_t>(0x80 | (c0 & 0x3F)); } else if ((c0 & 0x1F0000) != 0) { // U+10000〜U+10FFFF *out++ = static_cast<std::uint8_t>(0xF0 | (c0 >> 18)); *out++ = static_cast<std::uint8_t>(0x80 | ((c0 >> 12) & 0x3F)); *out++ = static_cast<std::uint8_t>(0x80 | ((c0 >> 6) & 0x3F)); *out++ = static_cast<std::uint8_t>(0x80 | (c0 & 0x3F)); } else { // そんな文字コードしるかぼけー throw 0; } return std::make_pair(it, out); } template<class InputIterator, class OutputIterator> std::pair<InputIterator, OutputIterator> utf8_to_utf16be(InputIterator it, OutputIterator out) { std::uint16_t c0 = *it++; if ((c0 & 0x80) == 0) { // U+0000〜U+007F *out++ = c0; } else if ((c0 & 0xE0) == 0xC0) { // U+0080〜U+07FF std::uint16_t c1 = valid_utf8_read(it); *out++ = ((c0 & 0x1F) << 6) | (c1 & 0x3F); } else if ((c0 & 0xF0) == 0xE0) { // U+0800〜U+FFFF std::uint16_t c1 = valid_utf8_read(it); std::uint16_t c2 = valid_utf8_read(it); *out++ = ((c0 & 0x0F) << 12) | ((c1 & 0x3F) << 6) | (c2 & 0x3F); } else if (0xF0 <= c0 && c0 <= 0xF4) { // U+10000〜U+10FFFF std::uint16_t c1 = valid_utf8_read(it); std::uint16_t c2 = valid_utf8_read(it); std::uint16_t c3 = valid_utf8_read(it); // 上位5bit c0 = ((c0 & 0x7) << 2) | ((c1 >> 4) & 0x3); if (c0 > 0x10) { // そんな文字コードしるかぼけー throw 1; } // サロゲートる *out++ = 0xD800 | ((c0 - 1) << 6) | ((c1 & 0x0F) << 2) | ((c2 >> 4) & 0x3); *out++ = 0xDC00 | ((c2 & 0x0F) << 6) | (c3 & 0x3F); } else { // そんな文字コードしるかぼけー throw 2; } return std::make_pair(it, out); } template<class InputIterator, class OutputIterator> std::pair<InputIterator, OutputIterator> utf16be_to_utf8(InputIterator it, OutputIterator out) { std::uint16_t c0 = *it++; // サロゲートペアかどうか調べる if ((c0 & 0xFC00) == 0xD800) { std::uint16_t c1 = *it++; // U+10000〜U+10FFFF std::uint32_t v = (((c0 & 0x03C0) + 0x0040) << 10) | ((c0 & 0x3F) << 10) | (c1 & 0x3FF); *out++ = static_cast<std::uint8_t>(0xF0 | (v >> 18)); *out++ = static_cast<std::uint8_t>(0x80 | ((v >> 12) & 0x3F)); *out++ = static_cast<std::uint8_t>(0x80 | ((v >> 6) & 0x3F)); *out++ = static_cast<std::uint8_t>(0x80 | (v & 0x3F)); } else if ((c0 & 0xFC00) == 0xDC00) { throw 0; // サロゲートの2バイト目とかふざけんなー } else if ((c0 & ~0x007F) == 0) { // U+0000〜U+007F *out++ = static_cast<std::uint8_t>(c0); } else if ((c0 & ~0x07FF) == 0) { // U+0080〜U+07FF *out++ = static_cast<std::uint8_t>(0xC0 | (c0 >> 6)); *out++ = static_cast<std::uint8_t>(0x80 | (c0 & 0x3F)); } else { // U+0800〜U+FFFF *out++ = static_cast<std::uint8_t>(0xE0 | (c0 >> 12)); *out++ = static_cast<std::uint8_t>(0x80 | ((c0 >> 6) & 0x3F)); *out++ = static_cast<std::uint8_t>(0x80 | (c0 & 0x3F)); } return std::make_pair(it, out); } template<class InputIterator, class OutputIterator> std::pair<InputIterator, OutputIterator> utf16be_to_utf32be(InputIterator it, OutputIterator out) { std::uint16_t c0 = *it++; // サロゲートペアかどうか調べる if ((c0 & 0xFC00) == 0xD800) { std::uint16_t c1 = *it++; // U+10000〜U+10FFFF *out++ = (((c0 & 0x03C0) + 0x0040) << 10) | ((c0 & 0x3F) << 10) | (c1 & 0x3FF); } else if ((c0 & 0xFC00) == 0xDC00) { throw 0; // サロゲートの2バイト目とかふざけんなー } else { // U+0000〜U+FFFF *out++ = c0; } return std::make_pair(it, out); } template<class InputIterator, class OutputIterator> std::pair<InputIterator, OutputIterator> utf32be_to_utf16be(InputIterator it, OutputIterator out) { std::uint32_t c0 = *it++; if ((c0 & ~0xFFFF) == 0) { // U+0000〜U+FFFF *out++ = static_cast<std::uint16_t>(c0); } else if (c0 <= 0x10FFFF) { // U+10000〜U+10FFFF // サロゲートる *out++ = static_cast<std::uint16_t>(0xD800 | ((((c0 & 0x1F0000) - 0x010000) | (c0 & 0x00FC00)) >> 10)); *out++ = static_cast<std::uint16_t>(0xDC00 | (c0 & 0x0003FF)); } else { // そんな文字コードしるかぼけー throw 2; } return std::make_pair(it, out); } #include <iostream> #include <vector> #include <cassert> #include <algorithm> int main() { // 「あいうえお」と何だかよく分からないサロゲートペア(U+10302)を追加したUTF-8の入力 const std::uint8_t input[] = { 0xE3, 0x81, 0x82, 0xE3, 0x81, 0x84, 0xE3, 0x81, 0x86, 0xE3, 0x81, 0x88, 0xE3, 0x81, 0x8A, 0xF0, 0x90, 0x8C, 0x82 }; // UTF8→UTF32→UTF8の変換テスト { std::vector<std::uint32_t> v; typedef std::pair<const std::uint8_t*, std::back_insert_iterator<std::vector<std::uint32_t> > > result_t; for (result_t r(input, std::back_inserter(v)); r.first != input + sizeof(input); r = utf8_to_utf32be(r.first, r.second)) ; std::vector<std::uint8_t> v2; typedef std::pair<std::vector<std::uint32_t>::const_iterator, std::back_insert_iterator<std::vector<std::uint8_t> > > result2_t; for (result2_t r(v.begin(), std::back_inserter(v2)); r.first != v.end(); r = utf32be_to_utf8(r.first, r.second)) ; assert(std::equal(input, input + sizeof(input), v2.begin())); } // UTF8→UTF16→UTF8の変換テスト { std::vector<std::uint16_t> v; typedef std::pair<const std::uint8_t*, std::back_insert_iterator<std::vector<std::uint16_t> > > result_t; for (result_t r(input, std::back_inserter(v)); r.first != input + sizeof(input); r = utf8_to_utf16be(r.first, r.second)) ; std::vector<std::uint8_t> v2; typedef std::pair<std::vector<std::uint16_t>::const_iterator, std::back_insert_iterator<std::vector<std::uint8_t> > > result2_t; for (result2_t r(v.begin(), std::back_inserter(v2)); r.first != v.end(); r = utf16be_to_utf8(r.first, r.second)) ; assert(std::equal(input, input + sizeof(input), v2.begin())); } // UTF8→UTF16→UTF32→UTF16→UTF8の変換テスト { std::vector<std::uint16_t> v; typedef std::pair<const std::uint8_t*, std::back_insert_iterator<std::vector<std::uint16_t> > > result_t; for (result_t r(input, std::back_inserter(v)); r.first != input + sizeof(input); r = utf8_to_utf16be(r.first, r.second)) ; std::vector<std::uint32_t> v2; typedef std::pair<std::vector<std::uint16_t>::const_iterator, std::back_insert_iterator<std::vector<std::uint32_t> > > result2_t; for (result2_t r(v.begin(), std::back_inserter(v2)); r.first != v.end(); r = utf16be_to_utf32be(r.first, r.second)) ; std::vector<std::uint16_t> v3; typedef std::pair<std::vector<std::uint32_t>::const_iterator, std::back_insert_iterator<std::vector<std::uint16_t> > > result3_t; for (result3_t r(v2.begin(), std::back_inserter(v3)); r.first != v2.end(); r = utf32be_to_utf16be(r.first, r.second)) ; std::vector<std::uint8_t> v4; typedef std::pair<std::vector<std::uint16_t>::const_iterator, std::back_insert_iterator<std::vector<std::uint8_t> > > result4_t; for (result4_t r(v3.begin(), std::back_inserter(v4)); r.first != v3.end(); r = utf16be_to_utf8(r.first, r.second)) ; assert(std::equal(input, input + sizeof(input), v4.begin())); } }
いろいろ纏められそうな部分はありそうだけど面倒なのでやらない。あとstatic_assertとかで弾かないとまずそうな部分もあったりするけど気にしない。
8to16と16to8は一度32を経由すればいいだけだし速度的にも大して変わらないだろうから必要無いような気がする。
とりあえず分かったのは、一度走査しないと出力の大きさが分からないっていうのはかなりでかい制限なんだなぁということ。出力は必ずback_inserter系のを使えっていうのも微妙だし、かといって出力のサイズを事前に求めようとするとInputIteratorだとダメになるし、どうすればいいのか分からなくなる。