TL;DR: UTF-8をデフォルトで使いたい人は環境変数に PYTHONUTF8=1
を設定しよう
Python は文字列が unicode なので、あちこちで「適切」なエンコーディングを選択する必要があります。残念ながら後方互換性やWindows固有の事情によりまだ ANSI Code Page (日本語なら cp932) がデフォルトで使われる場面があります。
ざっと Python と外の世界との入出力をあげてみます。
- テキストファイルを読み書きする時のデフォルトのエンコーディング = ACP
- 標準入出力のエンコーディング
- 標準入出力がコンソールのとき = UTF-16 で WriteConsoleW 等を呼ぶ
- 標準入出力がコンソールでない時 = ACP
- 子プロセスとのPIPE = ACP
最近 chcp 65001
を使って UTF-8 を使う方法が広まっているように思います。これはコンソールのエンコーディングを指定する (SetConsoleCP(), SetConsoleOutputCP()) ものなので、標準出力がリダイレクトされた場合には関係ありません。
# Power Shell 6
PS C:¥> python3 -c "print('おはよう')" > ps.txt
# cmd.exe
C:¥> chcp 65001
C:¥> python3 -c "print('おはよう')" > cmd.txt
同じコマンドに見えますが、 Power Shell 6 で出力した ps.txt は UTF-8 で書かれていて、 cmd.exe で出力した cmd.txt は cp932 で出力されました。 Power Shell は python3 の標準出力を一旦受け取り、 cp932 から UTF-8 に変換してリダイレクト先に書き込んでいるからです。
さて、 Python 3.7 からは最終兵器である UTF-8 mode (PEP 540) があります。このモードは Python を起動する時にコマンドラインオプションで -X utf8
を追加するか環境変数で PYTHONUTF8=1
を設定しておくと有効になります。このモードではエンコーディングは次のようになります。
- テキストファイルを読み書きする時のデフォルトのエンコーディング = UTF-8
- 標準入出力のエンコーディング
- 標準入出力がコンソールのとき = UTF-16 で WriteConsoleW 等を呼ぶ
- 標準入出力がコンソールでない時 = UTF-8
- 子プロセスとのPIPE = UTF-8
これで一貫して UTF-8 を使うことができます。
この状態では子プロセスとのパイプもデフォルトではUTF-8になってしまうので、cp932を使うコマンドを利用する&パイプをテキストモードで開くときにはエンコーディングに "mbcs" か "cp932" を指定する必要があります。
cp932 のままのコンソールを使う場合も、コンソール自体への入出力は WriteConsoleW が使われるので文字化けしないはずです。テキストファイルのエンコーディングがUTF-8に切り替わるのも多くの人にとってはメリットでしょう。 (type コマンドでそのファイルを表示すると文字化けしますが)
一方 PowerShell を使う場合、 python3.exe が標準出力に UTF-8 を書いているよと簡単に伝える手段が不明です。(この記事 を見つけたのですが、コマンドを実行するたびにこれをするのは...)
Python を Windows 上で使ってなるべく UTF-8 を使いたい人は次のような装備を揃えると良いでしょう。
- デフォルトで UTF-8 を利用するテキストエディタ (VS Code, Atom, Windows 10 1903 以降のメモ帳)
- cp65001 を利用するように設定した cmd.exe (あるいは勝手に cp932 -> UTF-8 変換しないシェル)
- 環境変数
PYTHONUTF8=1
を設定した Python 3.7 以降