はじめに
D言語で、各種文字コード(SJIS, EUC, JIS, EBCDIC, IBM漢字)への変換処理の実例を示したいと思います。
※記事で取り扱っていない文字コードについても、変換処理を実装する際の参考になればと思います。
環境
Shift_JIS(SJIS) <---> UTF8
toMBSz
、fromMBSz
使って、簡単に変換できます。Shift_JIS 文字コード表
string toSJIS(string s)
{ // UTF8 → SJIS
return ( s.toMBSz.to!string );
}
string fromSJIS(string s)
{ // SJIS → UTF8
return ( s.toStringz.fromMBSz );
}
EUC <---> UTF8
toMBSz
、fromMBSz
の引数にコードページを設定することで、変換できます。
コードページの対応表は、Code Page Identifiersにあります。20932
はEUC-JP
を意味しています。日本語EUC 文字コード表
string toEUC(string s)
{ // UTF8 → EUC(20932)
return ( s.toMBSz(20932).to!string );
}
string fromEUC(string s)
{ // EUC(20932) → UTF8
return ( s.toStringz.fromMBSz(20932) );
}
JIS <---> UTF8
JIS
からUTF8
に変換する場合、EUC
のようにfromMBSz
を使うだけでは変換できません。
なぜかというと、fromMBSz
では文字コードが0x80
以上の場合にのみ変換処理を行っています。
JIS
の漢字コードは0x80
未満で構成されるため、変換対象になりません。JIS漢字コード表(JIS X 0208)
string fromMBSz(return scope immutable(char)* s, int codePage = 0)
{
const(char)* c;
for (c = s; *c != 0; c++)
{
if (*c >= 0x80)
{
wchar[] result;
int readLen;
result.length = MultiByteToWideChar(codePage, 0, s, -1, null, 0);
if (result.length)
{
readLen = MultiByteToWideChar(codePage, 0, s, -1, result.ptr,
to!int(result.length));
}
if (!readLen || readLen != result.length)
{
throw new Exception("Couldn't convert string: " ~
sysErrorString(GetLastError()));
}
return result[0 .. result.length-1].to!string; // omit trailing null
}
}
return s[0 .. c-s]; // string is ASCII, no conversion necessary
}
そのため、fromCodePage
のような処理を実装する必要があります。
MultiByteToWideCharは、Windowsが提供しているAPIです。D言語は、Windows APIも簡単に呼び出せます。
string toJIS(string s)
{ // UTF8 → JIS(50220)
return ( s.toMBSz(50220).to!string );
}
string fromJIS(string s)
{ // JIS(50220) → UTF8
return ( s.fromCodePage(50220) );
}
string fromCodePage(string s, int codePage)
{ // (codePage) → UTF8
wstring ret;
int len = MultiByteToWideChar(codePage, 0, s.ptr, cast(int)s.length, null, 0);
if ( len > 0 ){
ret.length = len;
MultiByteToWideChar(codePage, 0, s.ptr, cast(int)s.length, cast(wchar*)ret.ptr, len);
}
return ( ret.to!string );
}
EBCDIC <---> UTF8
toMBSz
でも文字コードが0x80
以上かの判定があります。
このため、UTF8
からEBCDIC
に変換する際は、toCodePage
のような処理を実装する必要があります。
WideCharToMultiByteもWindows APIです。
EBCDIC
からUTF8
に変換する際は、JISと同じくfromCodePage
を使います。
20290
はIBM290
とも呼ばれ、CCSID 5026
と同じ変換結果になりました。EBCDIC(CCSID 5026)コード表
string toEBCDIC(string s)
{ // UTF8 → EBCDIC(20290)
return ( s.toCodePage(20290) );
}
string fromEBCDIC(string s)
{ // EBCDIC(20290) → UTF8
return ( s.fromCodePage(20290) );
}
string toCodePage(string s, int codePage)
{ // UTF8 → (codePage)
char[] ret;
wstring sw = s.to!wstring;
int len = WideCharToMultiByte(codePage, 0, sw.ptr, cast(int)sw.length, null, 0, null, null);
if ( len > 0 ){
ret.length = len;
WideCharToMultiByte(codePage, 0, sw.ptr, cast(int)sw.length, ret.ptr, len, null, null);
}
return ( ret.to!string );
}
IBM漢字(CP930) <---> UTF8
IBM漢字(CP930)は、MultiByteToWideCharやWideCharToMultiByteが未対応のため、コード変換できませんでした。
このため、変換テーブルを作成してコード変換処理を行うよう実装しました。
変換テーブル(cp930.txt)は、こちらの情報を元に作成させていただきました。
static class CP930TBL {
static private wstring CP930toUTF8 = import("cp930.txt");
static private uint[wchar] UTF8toCP930;
static this() {
UTF8toCP930 = initConvTbl();
}
static private uint[wchar] initConvTbl()
{
uint[wchar] cp930tbl;
foreach ( hi, line; CP930toUTF8.split("\n") ){
foreach ( lo, c; line ){
cp930tbl[c] = cast(uint)((hi << 8) + lo + 0x4040);
}
}
cp930tbl[' '] = 0x4040;
return ( cp930tbl );
}
static string to(string s)
{ // UTF8 → CP930
char[] ret;
foreach ( cw ; s.to!wstring ){
auto c1 = UTF8toCP930[cw];
ret ~= [cast(char)(c1 >> 8), cast(char)(c1 & 0xff)];
}
return ( ret.to!string );
}
static string from(string s)
{ // CP930 → UTF8
wchar[] ret;
for ( int i = 0; i < s.length ; i += 2 ){
int hi = s[i ] - 0x40;
int lo = s[i+1] - 0x40;
ret ~= CP930toUTF8[hi * (0xff - 0x3F + 1) + lo];
}
return ( ret.to!string );
}
}
alias toCP930 = CP930TBL.to;
alias fromCP930 = CP930TBL.from;
ソースコード実装例
これまでの処理をまとめた実装例です。蛇足ですが、ソースコード自体の文字コードはUTF-8
です。
import std.conv;
import std.stdio;
import std.string;
import std.windows.charset : fromMBSz, toMBSz;
import core.sys.windows.winnls : MultiByteToWideChar, WideCharToMultiByte;
string toSJIS(string s)
{ // UTF8 → SJIS
return ( s.toMBSz.to!string );
}
string fromSJIS(string s)
{ // SJIS → UTF8
return ( s.toStringz.fromMBSz );
}
string toCodePage(string s, int codePage)
{ // UTF8 → (codePage)
char[] ret;
wstring sw = s.to!wstring;
int len = WideCharToMultiByte(codePage, 0, sw.ptr, cast(int)sw.length, null, 0, null, null);
if ( len > 0 ){
ret.length = len;
WideCharToMultiByte(codePage, 0, sw.ptr, cast(int)sw.length, ret.ptr, len, null, null);
}
return ( ret.to!string );
}
string fromCodePage(string s, int codePage)
{ // (codePage) → UTF8
wstring ret;
int len = MultiByteToWideChar(codePage, 0, s.ptr, cast(int)s.length, null, 0);
if ( len > 0 ){
ret.length = len;
MultiByteToWideChar(codePage, 0, s.ptr, cast(int)s.length, cast(wchar*)ret.ptr, len);
}
return ( ret.to!string );
}
string toJIS(string s)
{ // UTF8 → JIS(50220)
return ( s.toMBSz(50220).to!string );
}
string fromJIS(string s)
{ // JIS(50220) → UTF8
return ( s.fromCodePage(50220) );
}
string toEUC(string s)
{ // UTF8 → EUC(20932)
return ( s.toMBSz(20932).to!string );
}
string fromEUC(string s)
{ // EUC(20932) → UTF8
return ( s.toStringz.fromMBSz(20932) );
}
string toEBCDIC(string s)
{ // UTF8 → EBCDIC(20290)
return ( s.toCodePage(20290) );
}
string fromEBCDIC(string s)
{ // EBCDIC(20290) → UTF8
return ( s.fromCodePage(20290) );
}
static class CP930TBL {
static private wstring CP930toUTF8 = import("cp930.txt");
static private uint[wchar] UTF8toCP930;
static this() {
UTF8toCP930 = initConvTbl();
}
static private uint[wchar] initConvTbl()
{
uint[wchar] cp930tbl;
foreach ( hi, line; CP930toUTF8.split("\n") ){
foreach ( lo, c; line ){
cp930tbl[c] = cast(uint)((hi << 8) + lo + 0x4040);
}
}
cp930tbl[' '] = 0x4040;
return ( cp930tbl );
}
static string to(string s)
{ // UTF8 → CP930
char[] ret;
foreach ( cw ; s.to!wstring ){
auto c1 = UTF8toCP930[cw];
ret ~= [cast(char)(c1 >> 8), cast(char)(c1 & 0xff)];
}
return ( ret.to!string );
}
static string from(string s)
{ // CP930 → UTF8
wchar[] ret;
for ( int i = 0; i < s.length ; i += 2 ){
int hi = s[i ] - 0x40;
int lo = s[i+1] - 0x40;
ret ~= CP930toUTF8[hi * (0xff - 0x3F + 1) + lo];
}
return ( ret.to!string );
}
}
alias toCP930 = CP930TBL.to;
alias fromCP930 = CP930TBL.from;
void writeHex(string s)
{ // 文字列の16進数表示
foreach ( c ; s ){
writef("%x ", c);
}
writeln();
}
void main()
{
string s = "あか青 AZaz ";
writeHex(s);
string s2 = s.toSJIS;
writeHex(s2);
writeHex(s2.fromSJIS);
s2 = s.toJIS;
writeHex(s2);
writeHex(s2.fromJIS);
s2 = s.toEUC;
writeHex(s2);
writeHex(s2.fromEUC);
s2 = "AZaz19アン<> ".toEBCDIC;
writeHex(s2);
writeHex(s2.fromEBCDIC);
s2 = "一9鸙煕 ".toCP930;
writeHex(s2);
writeHex(s2.fromCP930);
}
コンパイル手順
codeconv.d
と同じフォルダにcp930.txt
を配置してコンパイルします。
cp930.txt
は、こちらに保存しています。
dmd -m64 -J=. codeconv.d
または
ldc2 -J=. codeconv.d
exeファイル実行例
変換前後のコードを16進数で出力する例となります。
D:\Dev> codeconv
e3 81 82 e3 81 8b e9 9d 92 e3 80 80 41 5a 61 7a 20
82 a0 82 a9 90 c2 81 40 41 5a 61 7a 20
e3 81 82 e3 81 8b e9 9d 92 e3 80 80 41 5a 61 7a 20
1b 24 42 24 22 24 2b 40 44 21 21 1b 28 42 41 5a 61 7a 20
e3 81 82 e3 81 8b e9 9d 92 e3 80 80 41 5a 61 7a 20
a4 a2 a4 ab c0 c4 a1 a1 41 5a 61 7a 20
e3 81 82 e3 81 8b e9 9d 92 e3 80 80 41 5a 61 7a 20
c1 e9 62 b9 f1 f9 81 bd 4c 6e 40
41 5a 61 7a 31 39 ef bd b1 ef be 9d 3c 3e 20
45 41 42 f9 67 fe 68 85 40 40
e4 b8 80 ef bc 99 e9 b8 99 e7 85 95 e3 80 80
応用例
fromXXXとtoXXXを組み合わせる
例えば、Shift_JIS
からJIS
に変換したい場合は、fromSJIS
とtoJIS
を組み合わせて実装できます。
ソースコード実装例のcodeconv.d
から、main()
を差し替えてください。
UFCSで書くと、すっきりしますね。
void main()
{
string s1 = [0x82, 0xA0, 0x82, 0xA8, 0x82, 0xE2, 0x82, 0xDC]; // Shift_JISで"あおやま"
string s2 = s1.fromSJIS.toJIS;
s2.writeHex; // 1b 24 42 24 22 24 2a 24 64 24 5e 1b 28 42
}
ファイル入出力の実装例
同じく、ソースコード実装例のcodeconv.d
から、main()
を差し替えてください。
void main()
{
auto fi = File("sjis.txt", "rt");
scope(exit) fi.close();
auto fo = File("jis.txt", "wt");
scope(exit) fo.close();
string line;
while ( (line = fi.readln()) !is null ){
fo.write(line.fromSJIS.toJIS);
}
}
Shift_JIS
からJIS
への変換例として、Shift_JIS
で保存したインプットファイル(sjis.txt)を用意します。
あおやま
あかさか
参考情報
D言語でShift-JISを入出力する
PowerShellで文字コード変換
IBM漢字コードからUTF8やSJISに変換する表を手に入れたい→手に入れた
Code Page Identifiers
D言語のUFCSが好きだ!
この記事のサンプルコード、変換テーブル
各種文字コード表
Shift_JIS 文字コード表
日本語EUC 文字コード表
JIS漢字コード表(JIS X 0208)
EBCDIC(IBM290, CCSID 5026)コード表
IBM漢字(CP930)コード表