nislandのブログ

いろんなジャンルについて書くつもりです

PowerShell 7のコードページと$OutputEncodingと[Console]::OutputEncodingについて

背景

PowerShellをバージョン5から7にアップデートして、文字化け問題に悩まされました。

で、文字化けはとりあえず解消できたからいいものの、結局何が原因だったのかはいまいちよくわからない。
とりあえず検証したことをメモとして残しておこうって感じの記事です。

解決法

とりあえず先に解決法を。以下のコマンドで解決しました。

[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding('utf-8')

これをすれば文字化けが治るはずです。chcp 65001は必要なし。

PowerShell 5までの文字化けと対処法

utf-8で書かれたC++ソースをコンパイルして実行すると、日本語が含まれる標準出力は文字化けしてました。

これは、テキストがutf-8で書かれていたのに対し、コンソール上での文字エンコード方式がShift-jisとなっていたため、エンコーディングの不一致が生じていて文字化けが起きるというわけです(と思ってます)。

したがって、コンソール上のエンコーディング方式をutf-8に変えれば解決です。
これはchcp 65001でok。

PowerShell 7以降

chcp 65001では文字化けは解消しませんでした。

以下がutf-8エンコードされたソースコード

#include <iostream>

int main(){
    std::cout << "明日の天気は晴れです。\n";

    return 0;
}

実行結果が次。

譏取律縺ョ螟ゥ豌励・譎エ繧後〒縺吶・

コードページがデフォルトの932でも、65001でも同じように文字化けすることが確認できました。

文字化けを直す

Powershell 7ではコンソールへの標準出力の文字化けを直すために以下のコマンドを実行する必要があるようです。

[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding('utf-8')

このコードはコンソール上のエンコーディング方式をutf-8にしてくれるものです。

どうやら[Console]::OutputEncodingが、コンソール上のエンコード方式を表すPowershellの設定変数(Preference Variable)のようです。
これにutf-8にあたるエンコード方式を代入すれば解決なのですが、残念なことに[Console]::OutputEncoding = 'utf-8'などは通りません。

代入をできるようにするために[System.Text.Encoding]::GetEncoding()を'utf-8'にかましてあげます。
これでutf-8をコンソール上のエンコード方式に設定できます。

'utf-8'という文字列そのものが[Console]::OutputEncodingに代入できない原因は、型の違いにあるようです。
曰く、[Console]::OutputEncodingは「エンコード方式を示す型」のようなものであり、'utf-8'はstring型なので、型の不一致により代入ができないという......。
[System.Text.Encoding]::GetEncoding()はstring型を受け取りそれに対応する「エンコード方式を示す型」を返す関数なのでしょう。

検証

[Console]::OutputEncodingがどうやらコンソール上での文字エンコード方式を表しているということはわかりましたが、 だとしたらコードページとはいったい何なのか......

あともう一つ、似た名前の$OutputEncodingという設定変数も文字エンコードに関わっていそうですが、これもよくわからず。

とりあえず、コードページと$OutputEncoding、そして[Console]::OutputEncodingの値をいろいろと変えて検証してみました。
./aで上記ソースをコンパイルしたものの実行、catで上記ソースの表示、あとパス内の日本語名ディレクトリが文字化けするかどうかを調べます。

PowerShell 7の文字化け表

- chcp 932
$OE=utf-8
[Con]=sjis
65001
utf-8
sjis
932
utf-8
utf-8
65001
utf-8
utf-8
932
sjis
sjis
65001
sjis
sjis
932
sjis
utf-8
65001
sjis
utf-8
./a × × × ×
cat
パス

($OutputEncoding$OE[Console]::OutputEncoding[Con]に略記、一列目以降省略してます)

表中の×が文字化け、〇が正常に表示できている状態です。
こうしてみると、コードページと$OutputEncodingが特に機能している様子はなく、やはり[Console]::OutputEncodingがコンソールの文字エンコーディングを表しているようです。

こちらの記事を読むに、$OutputEncodingはパイプでデータを渡すときの文字エンコード方式のようですね。

ついでに、PowerShell 5でも同じことをテストしてみました。

PowerShell 5の文字化け表

- chcp 932
$OE=utf-8
[Con]=sjis
65001
utf-8
sjis
932
utf-8
utf-8
65001
utf-8
utf-8
932
sjis
sjis
65001
sjis
sjis
932
sjis
utf-8
65001
sjis
utf-8
./a × ×
cat × × × × × × × ×
パス

こっちはコードページが機能しているように見えます。catの文字化けが直っていないのが個人的に気になる。

[Console]::OutputEncodingがshift-jisでコードページも932のときは必ず文字化けしてます。
この状態で[Console]::OutputEncodingutf-8にするかコードページを65001にすると文字化けが解消しているので、やはりこの2つはコンソール上の文字エンコードにかんでそうな雰囲気がします。
左から5,6,7番目の例がわかりやすいです。

しかしcatの文字化けはこれらを設定するだけでは直りそうになかったです。どうするのがいいんだろう。

[2021/07/14] 文字化けを直すコマンドについて詳しい説明を記述していなかったため追記。