LoginSignup
18

More than 3 years have passed since last update.

posted at

updated at

Organization

そろそろWindowsでUTF-16とShift-JISの変換方法をC++erらしくまとめようか

Win32APIには御存知の通りWideCharToMultiByte関数とMultiByteToWideChar関数があるが、わりと使うのは面倒くさい。

そもそも当たり前だけどstd::basic_string<char_type>使えないし。

というわけでメモ代わりにまとめておく。locale周りはガバのプーさんだけどそこは勘弁して下さい。

結論

これでいいかと。なんとなくshrink_to_fit呼んでるけどこれ必要なのかねぇ

#include <string>
#include <windows.h>
#include <cstring>
std::wstring shift_jis_to_utf_16(const std::string& str)
{
    static_assert(sizeof(wchar_t) == 2, "this function is windows only");
    const int len = ::MultiByteToWideChar(932/*CP_ACP*/, 0, str.c_str(), -1, nullptr, 0);
    std::wstring re(len * 2 + 2, L'\0');
    if (!::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &re[0], len)) {
        const auto ec = ::GetLastError();
        switch (ec)
        {
        case ERROR_INSUFFICIENT_BUFFER:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INSUFFICIENT_BUFFER"); break;
        case ERROR_INVALID_FLAGS:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INVALID_FLAGS"); break;
        case ERROR_INVALID_PARAMETER:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INVALID_PARAMETER"); break;
        case ERROR_NO_UNICODE_TRANSLATION:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_NO_UNICODE_TRANSLATION"); break;
        default:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: unknown(" + std::to_string(ec) + ')'); break;
        }
    }
    const std::size_t real_len = std::wcslen(re.c_str());
    re.resize(real_len);
    re.shrink_to_fit();
    return re;
}
std::string utf_16_to_shift_jis(const std::wstring& str) {
    static_assert(sizeof(wchar_t) == 2, "this function is windows only");
    const int len = ::WideCharToMultiByte(932/*CP_ACP*/, 0, str.c_str(), -1, nullptr, 0, nullptr, nullptr);
    std::string re(len * 2, '\0');
    if (!::WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1, &re[0], len, nullptr, nullptr)) {
        const auto ec = ::GetLastError();
        switch (ec)
        {
        case ERROR_INSUFFICIENT_BUFFER:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INSUFFICIENT_BUFFER"); break;
        case ERROR_INVALID_FLAGS:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INVALID_FLAGS"); break;
        case ERROR_INVALID_PARAMETER:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: ERROR_INVALID_PARAMETER"); break;
        default:
            throw std::runtime_error("in function utf_16_to_shift_jis, WideCharToMultiByte fail. cause: unknown(" + std::to_string(ec) + ')'); break;
        }
    }
    const std::size_t real_len = std::strlen(re.c_str());
    re.resize(real_len);
    re.shrink_to_fit();
    return re;
}

雑記

CP_ACPってところを変えればいろいろ出来たりする。例えばISO_8859_1なら西ヨーロッパ言語 - iso-8859-1からutf-16への変換ができる。
cf.)

あと、WideCharToMultiByte関数は第二引数にWC_NO_BEST_FIT_CHARSを指定せずに呼ぶと似た文字への変換が発生するらしい。
cf.)

参考サイト

追記

ちょっと違うけど
http://qiita.com/AsladaGSX/items/af50066dbbfd59991af9
なんかすごい頑張っている記事を見つけました、

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
18