💬

WindowsとLinuxのsetlocale()

に公開

C/C++言語でエンコーディングを扱う標準的な方法はロケールになります。今回はそのロケールの設定/取得を行うsetlocale()を文字コードの観点からWindowsとLinuxで簡単に比較します。

0. ソースの在処

https://github.com/marudedameo2019/setlocale_examples

ビルド方法はreadme参照

1. setlocaleをデフォルトのロケールで設定する方法

現在のデフォルトのロケールを設定し、その文字列表現を表示するプログラムです。

https://github.com/marudedameo2019/setlocale_examples/blob/main/setlocale_default.c

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を設定する方法です。

https://github.com/marudedameo2019/setlocale_examples/blob/main/setlocale_utf8.c

※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に変えて出力してみています。

https://github.com/marudedameo2019/setlocale_examples/blob/main/utf8_emoji.c

$ ./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に変換して標準出力から出力します。

https://github.com/marudedameo2019/setlocale_examples/blob/main/def2utf8.c

システムロケールがシフトJISの環境でシフトJISを出力するhoge.cみたいなプログラムをMSYS2のコマンドとパイプで繋ぐときに間に挟むと文字化けが防げます。

$ ./setlocale_default | cat
▒▒▒݂̃▒▒P▒[▒▒: Japanese_Japan.932

$ ./setlocale_default | ./def2utf8 | cat
現在のロケール: Japanese_Japan.932

$ 

utf82defも同じ要領で簡単に作成できます。

GitHubで編集を提案

Discussion