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