WindowsとLinuxのsetlocale()
序
C/C++言語でエンコーディングを扱う標準的な方法はロケールになります。今回はそのロケールの設定/取得を行うsetlocale()を文字コードの観点からWindowsとLinuxで簡単に比較します。
0. ソースの在処
ビルド方法はreadme参照
1. setlocaleをデフォルトのロケールで設定する方法
現在のデフォルトのロケールを設定し、その文字列表現を表示するプログラムです。
1-1. Windows(Visual Studio)での実行結果
レジストリに設定されているシステムロケールを読み取り、それを設定、表示することになります。
ソースコードをU共通にしているので(UTF-8)、シフトJISでビルドするオプション(/execution-charset:.932)が指定してあります。
C:\...\Release>setlocale_default
現在のロケール: Japanese_Japan.932
C:\...\Release>
システムロケールの文字コード(シフトJIS)を示す文字列が表示されています。
1-2. Windows(MSYS2)での実行結果
レジストリに設定されているシステムロケールを読み取り、それを設定、表示することになります。
そのままコンパイルすると文字列リテラルがUTF-8になってしまい、システムロケールがデフォルトのシフトJISの場合文字化けするので、コンパイル時にシステムロケールに合わせた文字コード(シフトJIS)を-fexec-charsetで指定して文字列リテラルをシステムロケールに合わせています。
$ ./setlocale_default
現在のロケール: Japanese_Japan.932
$
システムロケールの文字コード(シフトJIS)を示す文字列が表示されています。
1-3. Linuxでの実行結果
LANGなどの環境変数を参照して設定、表示することになります。
$ ./setlocale_default
現在のロケール: ja_JP.UTF-8
$
UTF-8を示す文字列が表示されています。
1-4. まとめ
| 日本語Windows(VS) | 日本語Windows(MSYSS2) | Linux | |
|---|---|---|---|
| 結果 | システムロケールの文字コード(シフトJIS) | システムロケールの文字コード(シフトJIS) | UTF-8 |
| 設定元データ | レジストリ | レジストリ | 環境変数 |
| 文字列リテラルの文字コード | システムロケールの文字コード(シフトJIS) | システムロケールの文字コード(シフトJIS) | UTF-8 |
| コンパイルオプション | /execution-charset:.932(ソースコードの文字コードをシフトJISからUTF-8に変えてるため) | -fexec-charset=システムロケールの文字コード(シフトJIS) | 普通 |
2. setlocaleをUTF-8固定で設定する方法
環境変数もレジストリも関係なく無条件でUTF-8を設定する方法です。
※Windowsでは".UTF8"とすることでja_JPを現在の言語・地域を省略することが出来ますが、Linuxで出来ないのでフル指定しています。
2-1. Windows(Visual Studio)での実行結果
C:\...\Release>setlocale_utf8
現在のロケール: ja_JP.UTF-8
C:\...\Release>
やってみれば分かりますが、既存のWindowsコマンドとパイプで繋ぐと日本語を正しく処理できません。
C:\...\Release>setlocale_utf8 | findstr ロケール
C:\...\Release>
ロケールと出力しているはずなのに見つかっていません。UTF-8で出力しているため、シフトJISで探しても見つからないのです。
2-2. Windows(MSYS2)での実行結果
$ ./setlocale_utf8
現在のロケール: ja_JP.UTF-8
$
端末の出力エンコーディング設定はシフトJISのままですが、UCRTがUTF-8からシフトJISに自動的に変換出力しています。なので文字化けはしていませんがシフトJISにない文字は出力できないことに注意してください。
例えば絵文字などを出力しようとしてもシフトJISにはありません。以下では端末の出力エンコーディングをchcpでUTF-8に変えて出力してみています。
$ ./utf8_emoji
??
$ chcp.com 65001
Active code page: 65001
$ ./utf8_emoji
😀
$ chcp.com 932
現在のコード ページ: 932
$
2-3. Linuxでの実行結果
出力は変わりません。環境変数を見なくなるだけです。
$ ./setlocale_utf8
現在のロケール: ja_JP.UTF-8
$
2-4. まとめ
| 日本語Windows(VS) | 日本語Windows(MSYSS2) | Linux | |
|---|---|---|---|
| 結果 | UTF-8 | UTF-8 | UTF-8 |
| 設定元データ | 固定値 | 固定値 | 固定値 |
| 文字列リテラルの文字コード | UTF-8 | UTF-8 | UTF-8 |
| コンパイルオプション | 普通(ソースコードの文字コードをシフトJISからUTF-8に変えてるため) | 普通 | 普通 |
※既存のWindowsコマンドとパイプで正しく繋げない
※端末のエンコーディング設定がシフトJISだとシフトJISに含まれない文字は(UCRT経由だと)表示できない
3. デフォルトのロケールからUTF-8への変換サンプル
デフォルトロケールを設定して標準入力から文字列を読み込み、それをUTF-8に変換して標準出力から出力します。
システムロケールがシフトJISの環境でシフトJISを出力するhoge.cみたいなプログラムをMSYS2のコマンドとパイプで繋ぐときに間に挟むと文字化けが防げます。
$ ./setlocale_default | cat
▒▒▒݂̃▒▒P▒[▒▒: Japanese_Japan.932
$ ./setlocale_default | ./def2utf8 | cat
現在のロケール: Japanese_Japan.932
$
utf82defも同じ要領で簡単に作成できます。
Discussion