PythonとWindowsとUnicode
まあ、そうこうするうちにWindowsでもUTF-8デフォルトになりそうだけども。
とりあえず3.11頃の話。
デフォルトエンコーディング
locale.getencoding() で取れるデフォルトのエンコーディングは、Windowsだとシステムに設定したANSIコードページ依存
「地域」コントロールパネルの「Unicode対応ではないプログラムの言語」のやつ
open() で encoding を省略した場合など、多くの場面でこのエンコーディングが使われる
システムエンコーディングを無視してUTF-8を強制したかったら -Xutf8 や PYTHONUTF8=1 で UTF-8 Mode を有効にするとよい これはあくまでPythonコードの実行に影響するだけで、C拡張やサブプロセスに直接影響するものではない
*nix 環境向けには locale coercion という挙動もあり、こっちはCロケールみたいなのが設定されていた時に LC_CTYPE をUTF-8系のに変更する。こっちはPython本体だけでなく、C拡張やサブプロセスにも影響する。 ファイルシステム周り: POSIX系ではパスのネイティブな持ち方がバイト列なので、Pythonでもbytesで扱うコードがちょいちょいある。一方最近のWindowsはネイティブがUTF16なので、ACP経由だとエンコーディング変換が挟まり、事故る可能性がある。ので、UTF16にすれば安心でしょ?という話らしい。 https://peps.python.org/pep-0529/ Windowsでバイト指向インターフェイスだと完全には保持できないってのは、ACP系のWinAPIを使うからそこで壊れるって話なんだろうか?Unicode系のWinAPIでやりとりして、内部はUTF8ででも持てばという気もするが…。
コンソールとリダイレクト
前述の通り、3.11あたりでもstdioは基本utf-8になっている。が、ConsoleAPIを使うという話からも推測されるように、stdioの先がファイルやパイプである場合はそうはならない。
code:stdio
C:\> python -c "import sys; [print(type(out.buffer.raw), out.isatty(), out.encoding, out.errors) for out in sys.stdout,sys.stderr]" <class '_io._WindowsConsoleIO'> True utf-8 surrogateescape
<class '_io._WindowsConsoleIO'> True utf-8 backslashreplace
C:\>python -c "import sys; [print(type(out.buffer.raw), out.isatty(), out.encoding, out.errors) for out in sys.stdout,sys.stderr]" 2>&1 | more <class '_io.FileIO'> False cp1252 surrogateescape
<class '_io.FileIO'> False cp1252 backslashreplace
手入力や端末出力なら日本語扱えるのに、入力や出力をパイプした途端に死ぬ、ということが起きる
ACPと互換性のない文字列を入出力する場合…
stdinはしれっと化けてエラーにはならない
surrogateescape がエンコードしてくれないの?というと、ANSI系APIを使う時点で落ちてる(気がする)
stdoutはerrorsが surrogateescape なので UnicodeEncodeError になる
stderrはerrorsが backslashreplace なので \uxxxx の形で出力され、エラーにはならない
余談: コンソールのコードページ
ここまでコードページというとGetACPで取れるシステムロケールの話をしていたが、Windowsのコンソールはそれとは別のコードページ設定を持っている(GetConsoleCP, GetConsoleOutputCP)
多分…WindowsConsoleIO(中身ConsoleWriteW等々)を使う今のPythonはほとんど関係ない話だと思っているが…昔は関係あったかもしれない
stdioがpipeにつながっている場合に使われるのもこれではなく locale.getencoding() っぽいし
chcpというと、pyenv-winを使うと勝手に chcp 1250 される
これが原因でコマンドライン引数に渡した文字列が化けるとかいう話を見かけたが再現しない