文字コード変換
.NETの世界では文字列としてはUnicodeだけ扱えればたいてい事足りるのですが、外の世界、たとえばテキストファイルではShift-JISやEUC-JP、電子メールではJIS(iso-2022-jp)が使われています。このようなテキストを扱うには、必要に応じて文字コードの変換を施さなくてはなりません。C#やVB.NETでテキストファイルを作ってみましょう。たとえばC#なら:
// C#でテキストファイルを作る
public class Program {
public static void Main() {
string contents = "4行の,文字列を,ファイルに,書きました";
System.IO.File.WriteAllLines("trial.txt", contents.Split(','));
}
}
このとき、ファイル内の文字列の文字コードはUTF-8です。UTF-8以外の文字コードでテキストファイルを読み書きするにはStreamReader、StreamWriterのコンストラクト時、引数にEncodingを与えます(※Encodingがサポートする文字コード):
// Shift-JISテキストファイルを読む
{
StreamReader reader(L"trial.txt", Encoding::GetEncoding(L"shift_jis"));
String^ line;
while ( (line = reader.ReadLine()) != nullptr ) {
Console::WriteLine(line);
}
}
扱う文字列がファイル上ではなくメモリ上にある場合は、文字コードを司るクラスEncodingのメソッドGetEncoder/GetDecoderによって、「Unicodeから変換するEncoder」「Unicodeに変換するDecoder」を手に入れることができます。Encoder/Decoderを使った文字コード変換クラスUnicodeConverterをC++/CLIで実装したのが以下のコードです:
/*
* System::Text::Encoder/Decoder による文字コード変換
*/
#include <string>
using namespace System;
ref class UnicodeConverter {
private:
System::Text::Encoding^ encoding_;
System::Text::Decoder^ decoder_;
System::Text::Encoder^ encoder_;
public:
UnicodeConverter(String^ name) {
encoding_ = System::Text::Encoding::GetEncoding(name);
encoder_ = nullptr;
decoder_ = nullptr;
}
UnicodeConverter(int codepage) {
encoding_ = System::Text::Encoding::GetEncoding(codepage);
encoder_ = nullptr;
decoder_ = nullptr;
}
std::string fromUnicode(const wchar_t* input, int size);
std::wstring toUnicode(const char* input, int size);
std::string fromUnicode(const std::wstring& input)
{ return fromUnicode(input.data(), input.size()); }
std::wstring toUnicode(const std::string& input)
{ return toUnicode(input.data(), input.size()); }
};
std::string UnicodeConverter::fromUnicode(const wchar_t* input, int size) {
int bytesUsed;
int charsUsed;
bool completed;
if ( encoder_ == nullptr ) encoder_ = encoding_->GetEncoder();
std::string result(size*5, '\0');
encoder_->Convert(
const_cast<wchar_t*>(input), size, // 変換元のポインタとサイズ
reinterpret_cast<unsigned char*>(&result[0]), result.size(), // 変換先のポインタとサイズ
true,
charsUsed, bytesUsed, completed);
result.resize(bytesUsed);
return result;
}
std::wstring UnicodeConverter::toUnicode(const char* input, int size) {
int bytesUsed;
int charsUsed;
bool completed;
if ( decoder_ == nullptr ) decoder_ = encoding_->GetDecoder();
std::wstring result(size,L'\0');
decoder_->Convert(
const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(input)), size, // 変換元のポインタとサイズ
&result[0], result.size(), // 変換先のポインタとサイズ
true,
bytesUsed, charsUsed, completed);
result.resize(charsUsed);
return result;
}
/*
* おためし
*/
#include <iostream>
#include <locale>
using namespace std;
int main() {
wcout.imbue(locale("japanese"));
// "JIS文字列" のJIS(iso-2022-jp)表現↓
string jis = "\x4A\x49\x53\x1B\x24\x42\x4A\x38\x3B\x7A\x4E\x73\x1B\x28\x42";
UnicodeConverter jis_converter(L"iso-2022-jp");
// JIS → UNICODE
wstring unicode = jis_converter.toUnicode(jis);
wcout << L'[' << unicode << L']' << endl;
// UNICODE → JIS
string result = jis_converter.fromUnicode(unicode);
if ( jis == result ) {
wcout << L"元の文字列と一致しました!" << endl;
}
// UNICODE → Shift-JIS
UnicodeConverter sjis_converter(L"shift_jis");
result = sjis_converter.fromUnicode(unicode);
cout << '[' << result << ']' << endl;
}
