Windows環境にて、C++からWin32 APIを呼び出して、テキストの文字コードをUTF-16とUTF-8の間で変換する方法を説明します。
開発環境 | Visual Studio 2022 |
---|---|
言語 | C++ |
フレームワーク | Win32 API |
動作確認 | Windows 11 Windows 10 Windows 7 |
C/C++を使用したWindowsプログラミングでは、通常、テキストの文字コードはUTF-16です。それに対して、オープンソースの便利なライブラリの多くは、WindowsやUNIX系OS等の複数のプラットフォームに対応するため、関数の引数や戻り値はUTF-8です。そのため、両者の変換が必要になります。
標準C++では、変換用のヘッダーとして<codecvt>が存在しますが、C++17にて非推奨になりました。よって、Win32 APIを使用する方法を以下に示します。
UTF-16からUTF-8
#include <windows.h>
// 厳密な方法
wchar_t utf16str[] = L"変換前のテキスト";
// 変換後のchar配列の要素数(null終端を含む)を取得
int size = WideCharToMultiByte(CP_UTF8, 0, utf16str, -1, nullptr, 0, nullptr, nullptr);
char *utf8str = new char[size];
// 変換
WideCharToMultiByte(CP_UTF8, 0, utf16str, -1, utf8str, size, nullptr, nullptr);
// ここで、変換後のテキストが入ったutf8strを使用
// 解放
delete[] utf8str;
// 簡易な方法
wchar_t utf16str[] = L"変換前のテキスト";
// 適当に大きい要素数で宣言する
char utf8str[100]{};
// 変換
WideCharToMultiByte(CP_UTF8, 0, utf16str, -1, utf8str, 100, nullptr, nullptr);
// 解放は不要
#include <windows.h>
#include <string>
// std::stringに格納する方法
wchar_t utf16str[] = L"変換前のテキスト";
// 変換後のchar配列の要素数(null終端を含む)を取得
int size = WideCharToMultiByte(CP_UTF8, 0, utf16str, -1, nullptr, 0, nullptr, nullptr);
// null終端を含まない要素数で宣言する
std::string utf8str(size - 1, '\0');
// 変換
WideCharToMultiByte(CP_UTF8, 0, utf16str, -1, &utf8str[0], size - 1, nullptr, nullptr);
// 解放は不要
UTF-8からUTF-16
#include <windows.h>
// 厳密な方法
char utf8str[] = u8"変換前のテキスト";
// 変換後のwchar_t配列の要素数(null終端を含む)を取得
int size = MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, nullptr, 0);
wchar_t *utf16str = new wchar_t[size];
// 変換
MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, utf16str, size);
// ここで、変換後のテキストが入ったutf16strを使用
// 解放
delete[] utf16str;
// 簡易な方法
char utf8str[] = u8"変換前のテキスト";
// 適当に大きい要素数で宣言する
wchar_t utf16str[100]{};
// 変換
MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, utf16str, 100);
// 解放は不要
#include <windows.h>
#include <string>
// std::wstringに格納する方法
char utf8str[] = u8"変換前のテキスト";
// 変換後のwchar_t配列の要素数(null終端を含む)を取得
int size = MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, nullptr, 0);
// null終端を含まない要素数で宣言する
std::wstring utf16str(size - 1, L'\0');
// 変換
MultiByteToWideChar(CP_UTF8, 0, utf8str, -1, &utf16str[0], size - 1);
// 解放は不要
ファイルのパスの場合
パスをUTF-16からUTF-8やShift_JISに変換するには、<filesystem>を使用するのが簡単です。プロジェクトのプロパティにて、[全般]-[C++言語標準]に「ISO C++17標準(/std:c++17)」以降を選択します。
#include <filesystem>
std::filesystem::path p = L"変換前のファイルのパス";
// UTF-8に変換
//p.u8string().c_str()
// Shift_JISに変換
//p.string().c_str()
UTF-8に対応したライブラリ関数でも、日本語を含んだパスを渡すとファイルのオープンに失敗することがあります。その場合は、Shift_JISに変換して渡してみましょう。
きちんとした関数は、内部処理にて「#ifdef _WIN32」して、引数のUTF-8をUTF-16に変換して、_wfopen_sやCreateFileに渡します。そうでない関数は、引数をそのままfopenに渡します。後者の場合、とりあえずはShift_JISで渡すと問題無いです。そして理想を言えば、前者のようにソース修正して、オープンソースのプロジェクトに貢献できたら素敵だと思います。
リファレンス
WideCharToMultiByte function
https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
MultiByteToWideChar function
https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar