【Python】ファイルへの書き込みで、文字コードで悩む場合は codecs で解決する件

以前より Teraterm 上から手動で Python コマンドを実行してプログラムを実行するのではなく、OS 上から Python プログラムを自動実行するプログラムを作成していました。

※CRONから実行するのではなくメール受信をトリガーに実行するプログラムです。

 

【Python】メール受信をトリガーとして【さくらVPS】サーバー上の Python プログラムをスタートしたい

 

 

どうしても文字コードエラーになる

OS から起動する Python プログラムのデバッグをするためにログをファイルに出力していたのですが、どうしても文字コードの関係でエラーになってしまい悩んでいました。

具体的に言うと、ファイルをオープンして write() でログをファイルに出力する際に

でエンコードエラーになりそのたびに「encode」や「decode」で文字コードを合わせていました。

しかしまだまだ Python の文字コードに詳しくないため、毎回エラーが出力されては「ここはencodeなのか?」「ここはdecodeなのか?」と考えてデバッグしていくのが大変でした。

 

ちなみに以下を試してみましたが、手動で Python コマンドを実行した時は問題ないのですが、OS から実行した際には環境が異なるため効果がありませんでした。

 

Rubyでプログラムを作っていた際には、正直言って一度も文字コードでエラーが出力されたことはなかったため、Python は文字コードだけで結構悩むことが多いです。

 

write() では Python のデフォルトの文字コードである「ASCII」で処理しようとする

マルチバイト文字でも Python は何も考慮せずに「ASCII」で処理しようとします。

そのため文字コードエラーになります。

これは仕様です。

 

 

 

標準出力をラップしても効果なし

以下のように最初に標準出力をラップしても効果はありませんでした。

 

■OSから実行の場合は効果なし

import sys, codecs
sys.stdout = codecs.EncodedFile(sys.stdout, 'utf_8')

 

 

■こちらもOSから実行の場合は効果なし

import sys, codecs
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)

 

OS から write()で文字列をファイルに出力されるときは、上記のようにラップしても効果はないのですが、Teraterm 上から Python コマンドを実行した際は問題なく動きました。

(正確に言うと、手動で Python コマンドから Python プログラムを実行した際は、文字コードエラーで悩むことはないが、OS から実行した時だけ、どうしても文字コードエラーで悩んでしまう)

 

そもそもファイルのオープン時に codecs で文字コードを指定すれば、ある意味ラップすることができた

以下のように codecs を利用して最初に文字コード(utf-8)を指定してファイルを開きます。

そして「write()」ではなく「print()」でファイルにログを出力します。

たったこれだけで文字コードエラーから解放されました。

 

以下のようにすると手動で Python コマンドを実行しても、OS から Python プログラムを実行しても文字コードエラーは出力されなくなりました。

 

■プログラム例

「UTF-8」でファイルを開く

# 最初に文字コードを指定して「追加」モードでログファイルを開く
f = codecs.open('/var/log/python/app.log', 'a', 'utf-8')

 

 

「print」でログを出力する

# 特殊文字でも問題なし

print('■■■■■■■■■■■■■■■■■■■', file=f)

 

# 日本語でも問題なし
print('デバッグテスト', file=f)

 

# 変数を経由しても問題なし

new_words = '記事の更新'
print(new_words, file=f)

 

 

この方法を知った時は文字コードエラーの悩みから若干解放されたので非常に嬉しく思いました。

(本当はもっと文字コードを本質的に理解する必要がありますが、それは今後の課題として)

 

そもそも codecs とは何をしているのか?

簡単に言うと「エンコード」と「デコード」をします。

(簡単すぎて正確ではないかもしれませんが)

 

■codecs.encode()

オブジェクトをエンコードします。

 

■codecs.decode()

オブジェクトをデコードします。

 

■codecs.open()

最初に文字コードを指定してファイルをオープンすることができます。

構文

codecs.open(filename, mode='r', encoding=None, errors='strict', buffering=1)

 

# 最初に文字コードを指定して「追加」モードでログファイルを開く
f = codecs.open('/var/log/python/app.log', 'a', 'utf-8')

 

# 以下のように明示的に指定することもできます。

f = codecs.open('/var/log/python/app.log', mode='a', encoding='utf-8')

 

 

 

 

Posted by 100%レンタルサーバーを使いこなすサイト管理人