目標
本記事では Windows 用 Visual Studio における C++ プロジェクト開発において、
文字コードをデフォルトの CP932 (Shift-JIS 派生) から UTF-8 に変更するための手順をまとめる。
※文字数のカウントや文字列の分割など、文字列データの中身を参照する操作については記載していない。
C++20 で追加された char8_t
を利用した、 UTF-8 固定のシステムの構築を目指す。
C++ 開発で文字コードが問題になるのは、大きく以下に分類される。
- ソースファイル自身の文字コード
- プログラムの入出力
- プログラム内の静的データ (文字列リテラル)
想定環境
- 検証時期: 2024 年 12 月
- Windows 11
- Visual Studio 2022 v17.12
- MSVC C++20 (執筆時点のバージョン)
特に C++20 で追加・変更された char8_t
型を使用する。
ソースファイル
C++ プロジェクトのプロパティに /utf-8
オプションを追加
デフォルト(CP932)では UTF-8 BOM無しソースファイルを使用すると、C4819 警告が発生する。
warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいま す。データの損失を 防ぐために、ファイルを Unicode 形式で保存してください。
/utf-8
オプションを追加することで、BOM無しの UTF-8 ソースファイルが正しく扱われるようになる。
- プロジェクトのプロパティ を開く
- 構成とプラットフォームを適宜選択
-
構成プロパティ > C/C++ > コマンドライン > 追加のオプション に次を追加:
/utf-8
※厳密には /utf-8
オプションではなく /source-charset:utf-8
オプションのみを指定すれば十分。
(補足) その他の関連プロパティ
-
構成プロパティ > 詳細 > 文字セット で
Unicode 文字セットを使用する
を選択- これは
_TCHAR
などのtchar.h
定義の仕組みを使用するための設定。この記事では UTF-8 を前提としているため、特に影響はない。 - [詳細] プロパティ ページ
- tchar.h での汎用テキスト マッピング
- これは
.editorconfig
で charset
を指定
.editorconfig
で charset
を指定すると、VisualStudio上で新規作成されるソースファイルの文字コードが指定されたものになる。
- Visual Studio ソリューションフォルダのルートに
.editorconfig
ファイルを作成し、以下を記述する。
root = true
[*.{c,cc,cpp,cxx,h,hh,hpp,hxx}]
charset = utf-8
EditorConfig で一貫性のあるコーディング スタイルを定義する
EditorConfig ドキュメント
プログラム入出力、静的データ
char
の代わりに char8_t
, std::string
の代わりに std::u8string
を使用する
char
, std::string
は文字コードについて何も保証しないため、代わりに char8_t
, std::u8string
を使用することで、UTF-8 文字列を扱うことをコード上で明確にする。
※前提として、プログラム間でやり取りされる文字列データに文字コードの区別はなく、単なるバイト列として扱われる。そのため、 std::u8string
が UTF-8 文字列データとして有効であるかは別の問題。
UTF-8エンコーディングされた文字の型として char8_t を追加 [P0482R6]
文字リテラルのプレフィックスを u8
にする
C++ コード内で文字リテラルを使用する場合は、 u8
プレフィックスを付けることで UTF-8 文字コードにエンコードされる。
Windows API で UTF-8 を扱う
例えば fopen
に対する _wfopen
など、Windows API ではワイド文字列 (Windows では UTF-16 を指す) を想定したC標準ライブラリ互換関数が用意されている。
こうしたワイド文字版API を呼び出す際には、UTF-8 文字列を UTF-16 に変換する必要がある。
※UTF-8 を直接渡す方法もある。: Use UTF-8 code pages in Windows apps
ここでは Windows API の MultiByteToWideChar
を使用した例を示すが、
utfcpp などのライブラリを使用した方が良い場面が多い。
#include <string>
#include <windows.h>
// UTF-8 バイナリ (終端文字あり) を UTF-16 に変換する一例
// Windows API に渡すため u16string ではなく wstring を返す
std::wstring to_wstr(const std::u8string& u8str) {
if (u8str.empty()) {
return std::wstring();
}
// calculate the size of the destination buffer
int wstr_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<const char*>(u8str.c_str()), static_cast<int>(u8str.size()), nullptr, 0);
// if the input is invalid, MultiByteToWideChar returns 0
if (wstr_size == 0) {
throw std::runtime_error("Invalid UTF-8 sequence");
}
std::wstring wstr(wstr_size, L'\0');
// convert UTF-8 to UTF-16
int result = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<const char*>(u8str.c_str()), static_cast<int>(u8str.size()), wstr.data(), static_cast<int>(wstr.size()));
// if the input is invalid, MultiByteToWideChar returns 0
if (result == 0) {
throw std::runtime_error("Invalid UTF-8 sequence");
}
return wstr;
}
MultiByteToWideChar 関数 (stringapiset.h)
Windows API の Unicode
ANSI版 Windows API で UTF-8 を扱う
fopen
など C標準ライブラリ互換の関数は、内部でANSI版 Windows API を使用している。
プログラム自体のコードページを変更することで、ANSI版 Windows API に UTF-8 文字列を渡すことができる。
T.B.D.
fopen, _wfopen
Use UTF-8 code pages in Windows apps
Windows/VC++ で char を UTF8 で扱う
ファイルパスは std::filesystem::path
を使用する。
ファイルパスに関しては std::filesystem::path
で、文字コードに依存せず文字列を扱うことができる。
path::native
, path::u8string
などのメンバ関数で、文字コードを変換することも可能。
コンソールのコードページを UTF-8 に変更する
プログラムをコンソールから呼び出す場合は、そのコンソール側のコードページ(使用する文字コード)を UTF-8 に変更する必要がある。
※コンソールを共有する他プロセスに影響を与える可能性があるため、注意が必要。
#include <windows.h>
int main() {
UINT in_codepage = GetConsoleCP();
UINT out_codepage = GetConsoleOutputCP();
// set codepage to UTF-8
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
// do something
// restore original codepage
SetConsoleCP(in_codepage);
SetConsoleOutputCP(out_codepage);
return 0;
}
関連記事
この記事の内容は、以下の関連記事と一部内容が重複する。
- Stack Overflow: Unicode 文字セットでも Shift-JIS で解釈される
- C++(Visual Studio)で UTF-8 を扱うための試行錯誤のメモ
- Windows/VC++ で char を UTF8 で扱う
参考文献
その他未整理
-
NTFS では、ファイル名が Unicode に格納されます。 これに対し、古い FAT12、FAT16、FAT32 ファイル システムでは、OEM 文字セットが使用されます。
- Unicode の文字数を書記素単位で数える
- C++コンソール出力にchar8_t文字列を出力したい!