📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
編集の要約なし |
|||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
Windows APIでは、wchar_t型はUTF-16に対応している。<br> | |||
Shift-JIS等のマルチバイト文字列からUTF-8へ直接変換することはできないため、1度UTF-16 (wchar_t型) を経由する必要がある。<br> | |||
<br> | |||
<u>※注意</u><br> | |||
<u>CP_ACPはシステムのデフォルトコードページを示し、日本語のWindowsでは通常Shift-JIS (CP932) だが、環境によって異なる場合がある。</u><br> | |||
<br><br> | <br><br> | ||
== | == 基本的な変換 == | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#include <windows.h> | #include <windows.h> | ||
char *pstrShiftJIS = "文字列のテスト"; | const char *pstrShiftJIS = "文字列のテスト"; | ||
wchar_t pstrUTF16[512] = { | wchar_t pstrUTF16[512] = {0}; | ||
char | char pstrUTF8[512] = {0}; | ||
// Shift-JISからUTF-16へ変換 | // Shift-JISからUTF-16へ変換 | ||
| 16行目: | 19行目: | ||
// UTF-16からUTF-8へ変換 | // UTF-16からUTF-8へ変換 | ||
::WideCharToMultiByte(CP_UTF8, 0, pstrUTF8, -1, pstrUTF16, | ::WideCharToMultiByte(CP_UTF8, 0, pstrUTF16, -1, pstrUTF8, 512, NULL, NULL); | ||
</syntaxhighlight> | |||
<br><br> | |||
== Shift-JISからUTF-8への変換 == | |||
以下の例では、バッファサイズの確認とエラーチェックを含む実装例を示している。<br> | |||
<syntaxhighlight lang="c++"> | |||
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; | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== UTF-8からShift-JISへの変換 == | |||
<syntaxhighlight lang="c++"> | |||
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; | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br><br> | ||
== std::string型を使用した変換 == | |||
std::string型を使用することにより、より安全で使いやすい実装が可能となる。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#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- | // UTF-8へ変換するために必要なバッファサイズを取得 | ||
int | int iLengthUTF8 = ::WideCharToMultiByte(CP_UTF8, 0, wstrUTF16.c_str(), -1, NULL, 0, NULL, NULL); | ||
if( | 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; | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | |||
== BOM (Byte Order Mark) == | |||
UTF-8では、ファイルの先頭にUTF-8であることを示す3バイトのBOM (Byte Order Mark) 0xEF、0xBB、0xBFが付加されている場合がある。<br> | |||
BOMが付加されていないUTF-8を、UTF-8NまたはBOM無しUTF-8と呼ぶ。<br> | |||
<br> | |||
* BOMの使い分け | |||
** BOM付きUTF-8 | |||
**: Windowsのメモ帳などで推奨。 | |||
**: 文字コードの自動判定に有効。 | |||
** BOM無しUTF-8 | |||
**: Unix/Linux環境、Webアプリケーション、XML、JSON等で推奨。 | |||
<br> | <br> | ||
* UTF-8のファイルを出力する際にBOMを付加する例 | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#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) | |||
if( | |||
{ | { | ||
ofs.write(reinterpret_cast<const char*>(UTF8_BOM), 3); | |||
} | |||
// UTF-8テキストを書き込む | |||
ofs.write(utf8Text.c_str(), utf8Text.size()); | |||
return ofs.good(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
C++ 17以降では、<code>std::filesystem</code> を使用してより安全な実装が可能である。<br> | |||
<syntaxhighlight lang="c++"> | |||
#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(); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
== | == BOMの検出 == | ||
以下の例では、ファイルからUTF-8を読み込む際にBOMを検出している。<br> | |||
<syntaxhighlight lang="c++"> | |||
#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無し | |||
} | |||
</syntaxhighlight> | |||
<br> | <br> | ||
C++ 17以降では、<code>std::filesystem</code> を使用してより安全な実装が可能である。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#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); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
== 注意事項 == | |||
* バッファサイズは、変換後の文字列が収まる十分なサイズを確保する必要がある | |||
* UTF-8では、1文字が最大4バイトになる可能性があるため、バッファサイズには余裕を持たせる | |||
* 変換に失敗する可能性があるため、戻り値の確認が重要 | |||
* CP_ACPは環境依存のため、特定のコードページを指定する場合はCP_932(Shift-JIS)などを使用する | |||
* MultiByteToWideCharおよびWideCharToMultiByteの第4引数に-1を指定すると、null終端文字を含めた変換が行われる | |||
* 変換できない文字がある場合、デフォルトでは'?'に置き換えられる(WideCharToMultiByteの最後の引数で制御可能) | |||
<br><br> | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:MFC]] | [[カテゴリ:MFC]] | ||