C++でstring型の文字コードを変換する方法

C++で日本語文字を取り扱ってたら1日が終わった。。

KST32BというストロークフォントをC++から使おうとしてたら色々とハマってしまったという話です。

KST32BはJISコードの文字のストロークフォントを保持してるのだけど、C++のstring型はUTF8の文字コードを取り扱っててどうやってJISコードに変換しようという問題でした。

Windows(Visual C++)の情報は色々出てくるんだけど、Mac(というかLinux)はなかなか情報出てこなかったので苦労しました。

こちらのURLに記載されている通りiconv.hを使えばいけるとのことです。ありがとうございます。

http://bebolog.blogspot.jp/2014/11/c.html

コードはコピペだけど転載いたします。

string convert_encoding(const std::string &str, const char *fromcode, const char *tocode)
{
    char *outstr, *instr;
    iconv_t icd;
    size_t instr_len  = std::strlen(str.c_str());
    size_t outstr_len = instr_len*2;
    
    if (instr_len <= 0) return "";
    
    // allocate memory
    instr  = new char[instr_len+1];
    outstr = new char[outstr_len+1];
    strcpy(instr, str.c_str());
    icd = iconv_open(tocode, fromcode);
    if (icd == (iconv_t)-1) {
        return "Failed to open iconv (" + std::string(fromcode) + " to " + std::string(tocode) + ")";
    }
    char *src_pos = instr, *dst_pos = outstr;
    if (iconv(icd, &src_pos, &instr_len, &dst_pos, &outstr_len) == -1) {
        // return error message
        std::string errstr;
        int err = errno;
        if (err == E2BIG) {
            errstr = "There is not sufficient room at *outbuf";
        } else if (err == EILSEQ) {
            errstr = "An invalid multibyte sequence has been encountered in the input";
        } else if (err = EINVAL) {
            errstr = "An incomplete multibyte sequence has been encountered in the input";
        }
        iconv_close(icd);
        return "Failed to convert string (" + errstr + ")";
    }
    *dst_pos = '\0';
    iconv_close(icd);
    
    std::string s(outstr);
    delete[] instr;
    delete[] outstr;
    
    return s;
}

Xcodeで開発してる場合は、libiconv.dylibをフレームワークに追加した上でiconv.hをインクルードしてください。 使い方は

string conv_str = convert_encoding(str, "utf-8", "euc-jp");

みたいな感じで第2引数に元の文字コード、第3引数に変換したい文字コードを渡してあげればOKです。

実際のJISコードへの変換ですが、こんな感じでやってます。

string convertUTF8ToJIS(const string &str){
   
    string conv_str = convert_encoding(str, "utf-8", "euc-jp");
    
    vector<int> jislist;
    int c = 0;
    for(int i=0;i<conv_str.size();i++){
        
        int tmp = conv_str[i]&0x7f;
        c = c<<8;
        c += tmp;
        
    }
    
    
    stringstream ss;
    ss<<hex<<c;
    string jis = ss.str();
    
    if(jis.size() < 4){
    
        int n_zero = 4 - jis.size();
        switch (n_zero) {
            case 1:
                jis = "0" + jis;
                break;
            case 2:
                jis = "00" + jis;
                break;
            
            case 3:
                jis = "000" + jis;
                break;
                
            default:
                break;
        }
        
    }
    
    // to Upper
    transform(jis.begin(), jis.end(), jis.begin(), ::toupper);
    
    
    return jis;
    
    
}

元々KST32Bを取り扱うpythonコードがあったのでそれを参考にしています。

http://boxheadroom.com/2009/06/03/kst

これでKST32BをC++から使えるようになりました。 KST32Bのストローク解析コードも書いたけど、基本上のpythonコードのC++版です。ニッチすぎるので需要はないと思いますが、欲しければご連絡いただければと思います。