「MFCの基礎 - UTF-8」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

編集の要約なし
173行目: 173行目:
**: 文字コードの自動判定に有効。
**: 文字コードの自動判定に有効。
** BOM無しUTF-8
** BOM無しUTF-8
**: Unix/Linux環境、Webアプリケーション、XML、JSON等で推奨。
**: Webアプリケーション、HTML、XML、JSON等で推奨。
<br>
<br>
* UTF-8のファイルを出力する際にBOMを付加する例
* UTF-8のファイルを出力する際にBOMを付加する例

2026年1月31日 (土) 20:18時点における版

概要

Windows APIでは、wchar_t型はUTF-16に対応している。
Shift-JIS等のマルチバイト文字列からUTF-8へ直接変換することはできないため、1度UTF-16 (wchar_t型) を経由する必要がある。

※注意
CP_ACPはシステムのデフォルトコードページを示し、日本語のWindowsでは通常Shift-JIS (CP932) だが、環境によって異なる場合がある。


基本的な変換

 #include <windows.h>
 
 const char *pstrShiftJIS = "文字列のテスト";
 wchar_t pstrUTF16[512] = {0};
 char pstrUTF8[512] = {0};
 
 // Shift-JISからUTF-16へ変換
 ::MultiByteToWideChar(CP_ACP, 0, pstrShiftJIS, -1, pstrUTF16, 512);
 
 // UTF-16からUTF-8へ変換
 ::WideCharToMultiByte(CP_UTF8, 0, pstrUTF16, -1, pstrUTF8, 512, NULL, NULL);



Shift-JISからUTF-8への変換

以下の例では、バッファサイズの確認とエラーチェックを含む実装例を示している。

 bool ShiftJISToUTF8(const char* pstrShiftJIS, char* pstrUTF8, int nUTF8BufferSize)
 {
    // UTF-16へ変換するために必要なバッファサイズを取得
    int iLengthUTF16 = ::MultiByteToWideChar(CP_ACP, 0, pstrShiftJIS, -1, NULL, 0);
    if(iLengthUTF16 == 0)
    {
       return false; // 変換失敗
    }
 
    // UTF-16用のバッファを確保
    wchar_t* pstrUTF16 = new wchar_t[iLengthUTF16];
    if(pstrUTF16 == NULL)
    {
       return false; // メモリ確保失敗
    }
 
    // Shift-JISからUTF-16へ変換
    if(::MultiByteToWideChar(CP_ACP, 0, pstrShiftJIS, -1, pstrUTF16, iLengthUTF16) == 0)
    {
       delete[] pstrUTF16;
       return false; // 変換失敗
    }
 
    // UTF-8へ変換するために必要なバッファサイズを取得
    int iLengthUTF8 = ::WideCharToMultiByte(CP_UTF8, 0, pstrUTF16, -1, NULL, 0, NULL, NULL);
    if(iLengthUTF8 == 0 || iLengthUTF8 > nUTF8BufferSize)
    {
       delete[] pstrUTF16;
       return false; // 変換失敗またはバッファ不足
    }
 
    // UTF-16からUTF-8へ変換
    if(::WideCharToMultiByte(CP_UTF8, 0, pstrUTF16, -1, pstrUTF8, nUTF8BufferSize, NULL, NULL) == 0)
    {
       delete[] pstrUTF16;
       return false; // 変換失敗
    }
 
    delete[] pstrUTF16;
    return true;
 }



UTF-8からShift-JISへの変換

 bool UTF8ToShiftJIS(const char* pstrUTF8, char* pstrShiftJIS, int nShiftJISBufferSize)
 {
    // UTF-16へ変換するために必要なバッファサイズを取得
    int iLengthUTF16 = ::MultiByteToWideChar(CP_UTF8, 0, pstrUTF8, -1, NULL, 0);
    if(iLengthUTF16 == 0)
    {
       return false; // 変換失敗
    }
 
    // UTF-16用のバッファを確保
    wchar_t* pstrUTF16 = new wchar_t[iLengthUTF16];
    if(pstrUTF16 == NULL)
    {
       return false; // メモリ確保失敗
    }
 
    // UTF-8からUTF-16へ変換
    if(::MultiByteToWideChar(CP_UTF8, 0, pstrUTF8, -1, pstrUTF16, iLengthUTF16) == 0)
    {
       delete[] pstrUTF16;
       return false; // 変換失敗
    }
 
    // Shift-JISへ変換するために必要なバッファサイズを取得
    int iLengthShiftJIS = ::WideCharToMultiByte(CP_ACP, 0, pstrUTF16, -1, NULL, 0, NULL, NULL);
    if(iLengthShiftJIS == 0 || iLengthShiftJIS > nShiftJISBufferSize)
    {
       delete[] pstrUTF16;
       return false; // 変換失敗またはバッファ不足
    }
 
    // UTF-16からShift-JISへ変換
    if(::WideCharToMultiByte(CP_ACP, 0, pstrUTF16, -1, pstrShiftJIS, nShiftJISBufferSize, NULL, NULL) == 0)
    {
       delete[] pstrUTF16;
       return false; // 変換失敗
    }
 
    delete[] pstrUTF16;
    return true;
 }



std::string型を使用した変換

std::string型を使用することにより、より安全で使いやすい実装が可能となる。

 #include <string>
 #include <windows.h>
 
 std::string ShiftJISToUTF8(const std::string& strShiftJIS)
 {
    // UTF-16へ変換するために必要なバッファサイズを取得
    int iLengthUTF16 = ::MultiByteToWideChar(CP_ACP, 0, strShiftJIS.c_str(), -1, NULL, 0);
    if(iLengthUTF16 == 0)
    {
       return ""; // 変換失敗
    }
 
    // UTF-16用のバッファを確保
    std::wstring wstrUTF16(iLengthUTF16, L'\0');
 
    // Shift-JISからUTF-16へ変換
    if(::MultiByteToWideChar(CP_ACP, 0, strShiftJIS.c_str(), -1, &wstrUTF16[0], iLengthUTF16) == 0)
    {
       return ""; // 変換失敗
    }
 
    // UTF-8へ変換するために必要なバッファサイズを取得
    int iLengthUTF8 = ::WideCharToMultiByte(CP_UTF8, 0, wstrUTF16.c_str(), -1, NULL, 0, NULL, NULL);
    if(iLengthUTF8 == 0)
    {
       return ""; // 変換失敗
    }
 
    // UTF-8用のバッファを確保
    std::string strUTF8(iLengthUTF8, '\0');
 
    // UTF-16からUTF-8へ変換
    if(::WideCharToMultiByte(CP_UTF8, 0, wstrUTF16.c_str(), -1, &strUTF8[0], iLengthUTF8, NULL, NULL) == 0)
    {
       return ""; // 変換失敗
    }
 
    // 末尾のnull文字を削除
    strUTF8.resize(iLengthUTF8 - 1);
 
    return strUTF8;
 }



BOM (Byte Order Mark)

UTF-8では、ファイルの先頭にUTF-8であることを示す3バイトのBOM (Byte Order Mark) 0xEF、0xBB、0xBFが付加されている場合がある。
BOMが付加されていないUTF-8を、UTF-8NまたはBOM無しUTF-8と呼ぶ。

  • BOMの使い分け
    • BOM付きUTF-8
      Windowsのメモ帳などで推奨。
      文字コードの自動判定に有効。
    • BOM無しUTF-8
      Webアプリケーション、HTML、XML、JSON等で推奨。


  • UTF-8のファイルを出力する際にBOMを付加する例
 #include <fstream>
 #include <cstring>
 
 // BOMの定義
 const unsigned char UTF8_BOM[3] = {0xEF, 0xBB, 0xBF};
 
 // ファイルへの書き込み例
 bool WriteUTF8File(const std::string& filename, const std::string& utf8Text, bool withBOM = true)
 {
    std::ofstream ofs(filename, std::ios::binary);
    if(!ofs)
    {
       return false;
    }
 
    // BOMを書き込む (必要な場合)
    if(withBOM)
    {
       ofs.write(reinterpret_cast<const char*>(UTF8_BOM), 3);
    }
 
    // UTF-8テキストを書き込む
    ofs.write(utf8Text.c_str(), utf8Text.size());
 
    return ofs.good();
 }


C++ 17以降では、std::filesystem を使用してより安全な実装が可能である。

 #include <fstream>
 #include <string>
 #include <filesystem>
 #include <vector>
 
 namespace fs = std::filesystem;
 
 bool WriteUTF8File(const fs::path& filepath, const std::string& utf8Text, bool withBOM = true)
 {
    std::ofstream ofs(filepath, std::ios::binary);
    if(!ofs)
    {
       return false;
    }
 
    // BOMを書き込む
    if(withBOM)
    {
       constexpr unsigned char UTF8_BOM[] = {0xEF, 0xBB, 0xBF};
       ofs.write(reinterpret_cast<const char*>(UTF8_BOM), sizeof(UTF8_BOM));
    }
 
    // UTF-8テキストを書き込む
    ofs << utf8Text;
 
    return ofs.good();
 }



BOMの検出

以下の例では、ファイルからUTF-8を読み込む際にBOMを検出している。

 #include <fstream>
 #include <cstring>
 
 bool HasUTF8BOM(const std::string& filename)
 {
    std::ifstream ifs(filename, std::ios::binary);
    if(!ifs)
    {
       return false;
    }
 
    unsigned char bom[3] = {0};
    ifs.read(reinterpret_cast<char*>(bom), 3);
 
    if(ifs.gcount() == 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF)
    {
       return true; // BOM付きUTF-8
    }
 
    return false; // BOM無し
 }


C++ 17以降では、std::filesystem を使用してより安全な実装が可能である。

 #include <fstream>
 #include <cstring>
 #include <filesystem>
 #include <vector>
 
 namespace fs = std::filesystem;
 
 bool HasUTF8BOM(const fs::path& filepath)
 {
    if(!fs::exists(filepath) || fs::file_size(filepath) < 3)
    {
       return false;
    }
 
    std::ifstream ifs(filepath, std::ios::binary);
    if(!ifs)
    {
       return false;
    }
 
    std::vector<unsigned char> bom(3);
    ifs.read(reinterpret_cast<char*>(bom.data()), 3);
 
    return (ifs.gcount() == 3 && 
            bom[0] == 0xEF && 
            bom[1] == 0xBB && 
            bom[2] == 0xBF);
 }



注意事項

  • バッファサイズは、変換後の文字列が収まる十分なサイズを確保する必要がある
  • UTF-8では、1文字が最大4バイトになる可能性があるため、バッファサイズには余裕を持たせる
  • 変換に失敗する可能性があるため、戻り値の確認が重要
  • CP_ACPは環境依存のため、特定のコードページを指定する場合はCP_932(Shift-JIS)などを使用する
  • MultiByteToWideCharおよびWideCharToMultiByteの第4引数に-1を指定すると、null終端文字を含めた変換が行われる
  • 変換できない文字がある場合、デフォルトでは'?'に置き換えられる(WideCharToMultiByteの最後の引数で制御可能)