いくら調べても全く分からなかった「Encode Error」ですがようやく解決しました。
ただし、まだまだ Python の Encode の構造がよく分かっていません。
Python 3.6 のエンコード
「Python 2系」と「Python 3系」はエンコードの仕様が異なるので注意です。
様々な Python プログラムの記事を掲載しているサイトを閲覧すると「Python 2系」と「Python 3系」が入り混じっています。
しかもまだまだ「Python 2系」のサイトが多いです。
Python 3系のエンコードに関してまとめると
- 「バイト列」と「文字列」は別物
- Python 3 では「文字列」は Unicode 文字列として扱われる
- 文字列(str型)→バイト列(bytes型)への変換は「’あ’.encode(‘utf-8’)」
- バイト列(bytes型)→文字列(str型)への変換は「’\u30cb\u30fc’.decode(‘utf-8’)」
- Encode 文字列→バイト列
- Decode バイト列→文字列
Python でファイルに書き込む場合
一般的なファイルを開いて書き込む方法です。
【プログラム例】
(pyenv) [test@SAKURA_VPS scraping]$ cat test7.py
f = open(‘/tmp/text.txt’,’w’)
(pyenv) [test@SAKURA_VPS scraping]$ |
【実行結果】
/tmp/text.txt を開いて内容を確認します。
(pyenv) [test@SAKURA_VPS scraping]$ python test7.py ← プログラムを実行します。 (pyenv) [test@SAKURA_VPS scraping]$ cat /tmp/text.txt |
ここまではまったく問題ありません。
しかし、これがスクリプトやOSから実行するプログラムになった場合に思いもよらない「エンコードエラー(Encode Error)」が出力されることになりました。
OSからプログラムを実行すると「UnicodeEncodeError: ‘ascii’ codec can’t encode character ~」が出力される
以前メール受信をトリガーとしてプログラムを起動する手順の記事を書きました。
【Python】メール受信をトリガーとして【さくらVPS】サーバー上の Python プログラムをスタートしたい
メール受信をきっかけに OS からプログラムを実行(つまり手動で Python コマンドを実行しない)したところ、以下のような「UnicodeEncodeError」が出力されました。
Dec 23 10:38:53 xxxxxx postfix/local[10479]: AF75E1E7295D: to=<test@mail.xxx.com>, relay=local, delay=12, delays=0.01/0/0/12, dsn=5.3.0, status=bounced (Command died with status 1: ” /home/test/pyenv/scraping/mailget.sh”. Command output: Traceback (most recent call last): File “/home/test/pyenv/scraping/mailget_timestamp.py”, line 133, in <module> tmpf.write(check) UnicodeEncodeError: ‘ascii’ codec can’t encode character ‘\u3042’ in position 0: ordinal not in range(128) ) |
標準出力をラップしても効果なし
OSから実行の場合は効果なし
import sys, codecs |
こちらもOSから実行の場合は効果なし
import sys, codecs |
どうしてもダメだった例
端末(Teraterm など)から Python コマンドを実行する場合は問題なくうまくいくのに、OS からプログラムを実行するとエラーになる例です。
f = open(‘/tmp/test.txt’, ‘w’)
f.write(check) |
以下のように「TypeError: write() argument must be str, not bytes」が出力されます。
Dec 23 13:32:41 xxxxxx postfix/local[13150]: F36331E7295D: to=<test@mail.xxxx.com>, relay=local, delay=13, delays=0.02/0.01/0/13, dsn=5.3.0, status=bounced (Command died with status 1: ” /home/test/pyenv/scraping/mailget.sh”. Command output: Traceback (most recent call last): File “/home/test/pyenv/scraping/mailget_timestamp.py”, line 135, in <module> f.write(check) TypeError: write() argument must be str, not bytes ) |
そのため、「byte」から「str」に変換すればいいのかと考え、以下のようにデコードしました。
f = open(‘/tmp/test.txt’, ‘w’)
f.write(check) |
しかし、実行すると「UnicodeEncodeError: ‘ascii’ codec can’t encode character ~」が出力されます。
Dec 23 13:35:05 xxxxxx postfix/local[13218]: 3A3EB1E7295D: to=<test@mail.xxxx.com>, relay=local, delay=13, delays=0.02/0.01/0/13, dsn=5.3.0, status=bounced (Command died with status 1: ” /home/test/pyenv/scraping/mailget.sh”. Command output: Traceback (most recent call last): File “/home/test/pyenv/scraping/mailget_timestamp.py”, line 135, in <module> f.write(check) UnicodeEncodeError: ‘ascii’ codec can’t encode character ‘\u3042’ in position 0: ordinal not in range(128) ) |
ここまで来ると堂々巡りですが、試しに再度エンコードをしてみます。
check = u’あ’.encode(‘utf-8’)
f.write(check) |
しかし、やはり「TypeError: write() argument must be str, not bytes」と出力されました。
Dec 23 13:40:16 xxxxxx postfix/local[13346]: 7C8811E7295D: to=<test@mail.xxxx.com>, relay=local, delay=13, delays=0.02/0.01/0/13, dsn=5.3.0, status=bounced (Command died with status 1: ” /home/test/pyenv/scraping/mailget.sh”. Command output: Traceback (most recent call last): File “/home/test/pyenv/scraping/mailget_timestamp.py”, line 135, in <module> f.write(check) TypeError: write() argument must be str, not bytes ) |
そもそも何が悪いのか?
write()は一体どうなっているのか?
write()に書き込むオブジェクトは「文字列(テキストモード)」か「バイト列(バイナリーモード)」に変換する必要がある
write()で書き込みができるのは
- 文字列(テキストモード)
- バイト列(bytes列)(バイナリ―モード)
の2つだけです。
ファイル操作のモード
- t : テキストモード(デフォルト)
- b : バイナリ―モード
【解決策】OSからプログラムを実行する場合は write() でファイルをバイト列(bytes型)でオープンすること
write()を調査したところ、モードが2つあります。
デフォルトは「テキストモード」です。
そもそも「bytes」にしろとメッセージが出力されているのでモードをバイナリモードに設定してみました。
f = open(‘/tmp/test.txt’, ‘wb’)
f.write(check) |
ファイルを開いて内容を確認します。
問題なく文字「あ」が表示されています。
(pyenv) [test@SAKURA_VPS scraping]$ cat /tmp/test.txt |
次は「u」を外しました。
f = open(‘/tmp/test.txt’, ‘wb’)
f.write(check) |
ファイルを開いて内容を確認します。
こちらも問題なく文字「あ」が表示されています。
(pyenv) [test@SAKURA_VPS scraping]$ cat /tmp/test.txt |
参考にしたサイト
ありがとうございます!
python; pickle にて TypeError: must be str, not bytes が出る場合
コメント