初老のボケ防止日記

おっさんのひとりごとだから気にしないようにな。

PythonでBASE64するときは注意しましょう。

Pythonで楽したつもりがハマった。
Pythonでテスト用のサーバを書いて動作確認してたんだけど、Python製のサーバをUbuntu上で動かしてる時は絶好調だったのに、Windows上で動かしたら動きません…というまあありがちな罠です。

実験

材料

とりあえずデータによって挙動が違うのかなと思ったので、バイナリデータとテキストデータを用意しましょう。バイナリデータはPythonのロゴ画像、テキストデータは何でもよかったんだけどロゴ画像をBASE64にしたものを使いましょう。

wget https://www.python.org/static/img/python-logo.png
cat python-logo.png | base64 > python-logo.txt

スクリプト

対象のデータをファイルから読み込んでBASE64化するスクリプト。ポイントはファイルを読み込むときのモードの違い。

モード 意味
rt 読込専用でテキストモード
rb 読込専用でバイナリモード
#! /usr/bin/env python
# -*- coding:utf-8 -*-

import os
import sys
import base64

def b64enc( target, mode ):
  return base64.b64encode(open(target,mode).read())

if __name__ == '__main__':
  if len(sys.argv) != 2:
    print 'Usage:python %s filename' % sys.argv[0]
    sys.exit(1)

  print 'reading file -> [%s] on [%s]' % (sys.argv[1], os.name)
  b1 = b64enc(sys.argv[1],'rt')
  b2 = b64enc(sys.argv[1],'rb')

  print 'b1 open("rt") b64encode data.len[%s]' % len(b1)
  print 'b2 open("rb") b64encode data.len[%s]' % len(b2)
  if b1 == b2 :
    print 'RESULT:b1 == b2'
  else:
    print 'RESULT b1 != b2'

動かしてみる

これをUbuntuとWindowsで動かしてみる

Ubuntu
$ ./b64.py python-logo.png
reading file -> [python-logo.png] on [posix]
b1 open("rt") b64encode data.len[13472]
b2 open("rb") b64encode data.len[13472]
RESULT:b1 == b2
$ ./b64.py python-logo.txt
reading file -> [python-logo.txt] on [posix]
b1 open("rt") b64encode data.len[18200]
b2 open("rb") b64encode data.len[18200]
RESULT:b1 == b2

どちらも問題ない。

Windows
$ ./b64.py python-logo.png
reading file -> [python-logo.png] on [nt]
b1 open("rt") b64encode data.len[8]
b2 open("rb") b64encode data.len[13472]
RESULT b1 != b2
$ ./b64.py python-logo.txt
reading file -> [python-logo.txt] on [nt]
b1 open("rt") b64encode data.len[18200]
b2 open("rb") b64encode data.len[18200]
RESULT:b1 == b2

バイナリデータをテキストモードで読み込むとダメ。

stackoverflow.com

結論

バイナリデータはバイナリモードで開きなさい*1

当たり前なんだけど、ちゃんとしないとダメってことで。

Pythonエンジニア養成読本[いまどきの開発ノウハウ満載!] (Software Design plus)

Pythonエンジニア養成読本[いまどきの開発ノウハウ満載!] (Software Design plus)

*1:テキストデータは改行コードとかあるからテキストモードのほうがいいのかもしれないけどそこは双方で改行コードは統一するのが普通なのかなとか思うとバイナリモードでもいい気がする