トホホな疑問(24) Python 3.8.0、chcp関係ない?

JosephHalfmoon

今回はPC上の「Microでない」Pythonネタです。昨年秋に、長年慣れ親しんだ2.7をようやく諦め、3.8.0に移行いたしました。Python2.7で、日本語文字コードを取り扱うことはそれほど多くなかったのですが、その度に、結構、苦労したような記憶があります。3.8.0はその辺、なんと堅牢なことか。とりあえず全部UTF-8にしておけばまず失敗がない。でもWindowsのcmd.exe デフォルトのコードページはCP932だよね。なんで大丈夫なの?

以下 Windows 10のcmd.exeの上でPython3.8.0動かしていての話です。

Python 3.x系に変更して以来、テキストファイルの処理でも日本語文字コードなどなどトラブルことがなかったので、意識が散漫になっておりました。UTF-8のファイルとはいえ、見た目ASCII、しかし僅かな日本語文字コードが混入していたファイルをencoding指定せず開いてしまい、ま、当然ですが例外発生しました。それ自体は当然なんでありますが、よく確かめもせずに漫然と使っていたので疑問がわいてきました。まとめると以下のような感じ。

  • cmd.exeのデフォルト文字コードはCP932(CP932は、ほぼほぼshift_jisという安易な理解。差の部分は気にしたことなし。)
  • その上でUTF-8で書いたPythonのスクリプトを走らせていて表示もOK
  • そういえば、cmd.exeの文字コードはCHCPコマンドで変更可能だった筈
  • CHCPコマンドで文字コードを65001(UTF-8の筈)に変えたらどうなる?
  • 画面表示とリダイレクトじゃ文字コード変わるでしょ。そちらはどうなる?

文字コードを反映している筈の以下の3つの返り値を条件別に確認しました。まず画面上。

$ chcp
現在のコード ページ: 932
~途中略~
sys.getfilesystemencoding() : utf-8
sys.stdout.encoding : utf-8
locale.getpreferredencoding() : cp932

ファイルシステムとstdoutがutf-8になっています。Python3.8.0のデフォルトね。しかし、cmd.exeのコードページはcp932なのにstdoutはutf-8なのね。不思議。けど localeみると cp932。openするときデフォルトはcp932ですな。

同じことをリダイレクトしてみると

sys.getfilesystemencoding() : utf-8
sys.stdout.encoding : cp932
locale.getpreferredencoding() : cp932

今度は、stdoutがcp932になりました。実際、リダイレクトされた先のファイルを見るとSJISです。

こんどはコードページを65001に変更してみます。

$ chcp
Active code page: 65001
~途中略~
sys.getfilesystemencoding() : utf-8
sys.stdout.encoding : utf-8
locale.getpreferredencoding() : cp932

うーむ。

コードページ932のときと、まったく変わらんです。しかし、日本語文字コードはコードページ932のときでも、コードページ65001のときでもちゃんと表示できているように見えます。便利だけれども、不思議。

CHCP関係ないじゃん。でもね、SJISとUTF-8のテストテキスト「あいうえお」をtypeしてみると コードページ65001のときは

$ type testSJIS.txt
・・・・・・・・・・

$ type testUTF8.txt
あいうえお

コードページ932のときは

$ type testSJIS.txt
あいうえお


$ type testUTF8.txt
縺ゅ>縺・∴縺・

コードページ、ちゃんと効いていますよ。しかし、この変化に「動じない」Python3.8.0は偉い。

そこで上の2つのファイルを encoding=”utf-8″, encoding=”shift-jis”, そしてエンコード指定なしの3つの方法で開いてみました。cmd.exeのコードページは932と65001の2つ、画面上とリダイレクトの2種、その上、デフォルトのファイルシステムエンコードをutf-8からmbcsに「戻して」しまう

sys._enablelegacywindowsfsencoding()

も使ってみました。しかし、申し上げます。どのケースでもまったく同じ結果を得ました。

あいうえお
readUTF8 - File UTF8 : True

'utf-8' codec can't decode byte 0x82 in position 0: invalid start byte
readUTF8 - File SJIS : False

'cp932' codec can't decode byte 0x86 in position 8: illegal multibyte sequence
readNENC - File UTF8 : False

あいうえお
readNENC - File SJIS : True

あいうえお
readSJIS - File SJIS : True

'shift_jis' codec can't decode byte 0x86 in position 8: illegal multibyte sequence
readSJIS - File UTF8 : False

UTF-8のファイルはencoding=”utf-8″のとき正しく読めて、後は例外発生。SJISのファイルは、encoding=”shift_jis”またはencoding指定なしのときに正しくよめて、他は例外発生。なお、encoding指定なしのときは、細かく言うとshift_jisではなくcp932であります。

状況に関わらず、正しくエンコーディング指定すれば正しく読める

堅牢です。Python3.8 は良いんですけどね、Python3.7、どうもCHCPに影響受けている気がする。そこのあたりがどうもトホホな感じ、また次回か。。。

トホホな疑問(23) 今、StdOutどこ向いている? へ戻る

トホホな疑問(25) Python, argparse, help?? へ進む