ICU 2.x : UnicodeString による文字コード変換
ICU 2.x ではUNICODEによる文字列クラス UnicodeString
が導入されました。
UnicodeString
はマルチバイト文字列からコンストラクトでき、さらにメソッド
extract
でマルチバイト文字列に変換できます。
このふたつを用いて任意の文字コード間の変換を行なってみましょう。
マルチバイト文字列 → UnicodeString
マルチバイト文字列からUnicodeString
すなわちUNICODEに変換するには:
UConverter
を生成し、UnicodeString
のコンストラクタに与える- codepage文字列を
UnicodeString
のコンストラクタに与える
の2通りの方法があります。後者の方が実装が簡単ですが、これはUnicodeString
のコンストラクタ内でUConverter
を生成/変換後に破棄していますから、パフォーマンスは若干劣ります。
-
UnicodeString::UnicodeString(const char* codepageData, const char* codepage =0);
codepage
で表現されたマルチバイト文字列codepageData
からUnicodeString
を生成します。[2] -
UnicodeString::UnicodeString(const char* src, int32_t srcLength, UConverter* cnv, UErrorCode& errorCode);
cnv
を用いてsrc
から始まるsrcLength
バイトのマルチバイト文字列からUnicodeString
を生成します。[3]※ 変換で発生したエラーは
errorCode
に設定されます。
UnicodeString
→ マルチバイト文字列
UnicodeString
からマルチバイト文字列に変換するには:
UConverter
を生成し、メソッドextract
に与える- codepage文字列をメソッド
extract
に与える
の2通りの方法があります。後者の方が実装が簡単ですが、コンストラクタと同様、メソッド内でUConverter
を生成/変換後に破棄していますから、パフォーマンスは若干劣ります。
-
int32_t UnicodeString::extract(int32_t start, int32_t startLength, char* target, const char* codepage =0) const;
start
から始まるstartLength
文字をcodepage
で表現されたマルチバイト文字列に変換しtarget
に格納します。[5] -
int32_t UnicodeString::extract(char* dest, int32_t destCapacity, UConverter* cnv, UErrorCode& errorCode) const;
cnv
を用いてUNICODE文字列全体をマルチバイト文字列に変換し、dest
から始まるdestCapacity
バイトの領域に格納します。[7]※ 変換で発生したエラーは
errorCode
に設定されます。
いずれのメソッドも、変換後のバイト数を返します。 格納先を 0にしておけば、実際の変換を行なわず、変換結果の格納に必要な領域の大きさを返します(ただし末尾の ‘\0’を含まない)。[4] [6]
std::string
のコンストラクタであらかじめ必要な大きさを与えることにより[8] [10]、char[]バッファを明示的に確保せず、std::string
をUNICODE→マルチバイト文字列変換の格納先に直接指定することもできます[9][11]。
このとき、変換結果を格納するのに必要な容量をあらかじめ求めておかなければならないため、メソッドextract
を2度呼ばなければなりません。
が、ある程度の余裕をもって十分な領域を確保しておくことができる場合もあります。
たとえば shift_jis に変換する場合、UNICODEでの1文字は shift_jisでは1もしくは2バイトに変換されますから、UNICODE文字列の文字数の2倍のバイト数の領域を確保しておけば十分です。
UnicodeString input; std::string result(input.length()*2, '\0'); // 十分な長さの '\0' で埋まった文字列 input.extract(0, input.length(), &result[0], "shift_jis"); result.erase(result.find('\0')); // 末尾の余分な '\0' を削除
CppUnitによるサンプル
#include <unicode/unistr.h> // UnicodeString #include <unicode/ucnv.h> // UConverter #include <cppunit/extensions/HelperMacros.h> #include <cppunit/TestAssert.h> /* CppUnitのための * UnicodeString の比較と文字列化 */ template<> struct CppUnit::assertion_traits<icu::UnicodeString> { static bool equal(const icu::UnicodeString& x, const icu::UnicodeString& y) { return x == y ? true : false; } static std::string toString(const icu::UnicodeString& x) { std::string result(x.extract(0, x.length(), 0, "shift_jis"), '\0'); x.extract(0, x.length(), &result[0], "shift_jis"); return result; } }; class UnicodeStringTest : public CppUnit::TestFixture { private: UConverter* converter; UErrorCode error; static std::wstring uinput; static std::string sinput; public: virtual void setUp() { error = U_ZERO_ERROR; converter = ucnv_open("shift_jis", &error); CPPUNIT_ASSERT( U_SUCCESS(error) ); } virtual void tearDown() { ucnv_close(converter); } /* from UNICODE UnicodeString::UnicodeString(const UChar* text); */ void testCtor0() { icu::UnicodeString result(uinput.c_str()); // [1] CPPUNIT_ASSERT_EQUAL(6L, result.length()); for ( int i = 0; i < result.length(); ++i) { CPPUNIT_ASSERT( uinput[i] == result[i]); } } /* from shift_jis using codepage UnicodeString::UnicodeString(const char* codepageData, const char* codepage =0); */ void testCtor1() { icu::UnicodeString expected(uinput.c_str()); icu::UnicodeString result(sinput.c_str(), "shift_jis"); // [2] CPPUNIT_ASSERT_EQUAL( expected, result); } /* from shift_jis using UConverter UnicodeString::UnicodeString(const char* src, int32_t srcLength, UConverter* cnv, UErrorCode& errorCode); */ void testCtor2() { icu::UnicodeString expected(uinput.c_str()); icu::UnicodeString result(sinput.c_str(), sinput.size(), converter, error = U_ZERO_ERROR); // [3] CPPUNIT_ASSERT( U_SUCCESS(error) ); CPPUNIT_ASSERT_EQUAL( expected, result); } /* to shift_jis using codepage int32_t UnicodeString::extract(int32_t start, int32_t startLength, char* target, const char* codepage =0) const; */ void testConvert0() { const std::string expected = sinput; icu::UnicodeString input(uinput.c_str()); int32_t convertedLength = input.extract(0, input.length(), 0, "shift_jis"); // [4] char* result = new char[convertedLength + 1]; input.extract(0, input.length(), result, "shift_jis"); // [5] CPPUNIT_ASSERT_EQUAL( expected, std::string(result) ); delete[] result; } /* to shift_jis using converter int32_t UnicodeString::extract(char* dest, int32_t destCapacity, UConverter* cnv, UErrorCode& errorCode) const; */ void testConvert1() { const std::string expected = sinput; icu::UnicodeString input(uinput.c_str()); int32_t convertedLength = input.extract(0, 0, converter, error = U_ZERO_ERROR); // [6] char* result = new char[convertedLength+1]; input.extract(result, convertedLength+1, converter, error = U_ZERO_ERROR); // [7] CPPUNIT_ASSERT( U_SUCCESS(error) ); CPPUNIT_ASSERT_EQUAL( expected, std::string(result) ); delete[] result; } /* to shift_jis no-using buffer */ void testConvert2() { const std::string expected = sinput; icu::UnicodeString input(uinput.c_str()); { // using codepage std::string result(input.extract(0, input.length(), 0, "shift_jis"), '\0'); // [8] input.extract(0, input.length(), &result[0], "shift_jis"); // [9] CPPUNIT_ASSERT_EQUAL( expected, result); } { // using UConverter std::string result(input.extract(0, 0, converter, error = U_ZERO_ERROR), '\0'); // [10] input.extract(&result[0], result.size(), converter, error = U_ZERO_ERROR); // [11] CPPUNIT_ASSERT( U_SUCCESS(error) ); CPPUNIT_ASSERT_EQUAL( expected, result); } } /* euc-jp to shift_jis */ void testConvert3() { std::string euc_jp = "\xC6\xFC\xCB\xDC"; std::string shift_jis = "日本"; UConverter* fromConverter = ucnv_open("euc-jp", &error); UConverter* toConverter = ucnv_open("shift_jis", &error); CPPUNIT_ASSERT( U_SUCCESS(error) ); icu::UnicodeString ustr(euc_jp.c_str(), euc_jp.size(), fromConverter, error = U_ZERO_ERROR); // [12] std::string result(ustr.extract(0, 0, toConverter, error = U_ZERO_ERROR), '\0'); ustr.extract(&result[0], result.size(), toConverter, error = U_ZERO_ERROR); ucnv_close(fromConverter); ucnv_close(toConverter); CPPUNIT_ASSERT_EQUAL( shift_jis, result ); } CPPUNIT_TEST_SUITE(UnicodeStringTest); CPPUNIT_TEST(testCtor0); CPPUNIT_TEST(testCtor1); CPPUNIT_TEST(testCtor2); CPPUNIT_TEST(testConvert0); CPPUNIT_TEST(testConvert1); CPPUNIT_TEST(testConvert2); CPPUNIT_TEST(testConvert3); CPPUNIT_TEST_SUITE_END(); }; std::wstring UnicodeStringTest::uinput = L"ガンバレ日本"; std::string UnicodeStringTest::sinput = "ガンバレ日本"; CPPUNIT_TEST_SUITE_REGISTRATION(UnicodeStringTest);