

python のpickleでファイルを保存する場合、バイナリモードで保存することは必須なのでしょうか? 例えば、

[code]

    import pickle
    import numpy as np
    x = np.random.random(100,100,100)
    
    # binary mode
    pickle.dump(x, open('hoge.pkl', 'wb'))
    # not binary mode
    pickle.dump(x, open('fuga.pkl', 'w'))
    
[/code]

とすると、、'wb' オプションでも、'w'オプションでも、全く同じ出力ファイルが得られるようです。(diffをとると全く差がない)

質問: この 'b' (binary option) を付けないとまずいような状況は何かあるのでしょうか?  
'b'をつけるか付けないかで、保存されるものは変わりうるのでしょうか?

ちなみに、私は Linux (Ubuntu) を使っています。


CC BY-SA 3.0
2015-11-18T09:59:06.357
18992
2015-11-18T14:21:03.433
2015-11-18T10:15:31.383
848
848
question
python
python pickleのファイルモード (バイナリモードの必然性)


Python3 では`bytes`という型が出来て、`str`と区別されるようになりました。  
テキストモードで開かれたファイルオブジェクトの`write`は`str`を要求するので、質問のコードは失敗します。

[code]

    >>> pickle.dump(x, open('fuga.pkl', 'w'))
    Traceback (most recent call last):
      File "<ipython-input-27-c8e3989f5c8e>", line 1, in <module>
        pickle.dump(x, open('fuga.pkl', 'w'))
    TypeError: must be str, not bytes
    
[/code]

Python2
ではエラーにはなりませんが、テキストモードではデータに改行文字があると、OS標準の改行コードに変更されるので、内容が変わる事がある筈です。pickleが作るデータに改行文字が含まれていて、それの変更がデータのロード時に影響するかどうかは私は分かりません。


CC BY-SA 3.0
2015-11-18T11:12:59.010
18997
2015-11-18T11:12:59.010
3054
18992
answer


以降、Python 2.7.10 を前提に話をします。

Python の `open()` 関数ですが、実体は `fileobject.c` の `file_init()` 関数です。

**python2.7.10/Objects/fileobject.c:file_init()**

[code]

    file_init(PyObject *self, PyObject *args, PyObject *kwds)
                      :
      if (open_the_file(foself, name, mode) == NULL)
    
[/code]

**python2.7.10/Objects/fileobject.c:open_the_file()**

[code]

    open_the_file(PyFileObject *f, char *name, char *mode)
              :
    #ifdef MS_WINDOWS
              :
      f->f_fp = _wfopen(PyUnicode_AS_UNICODE(f->f_name),
                        PyUnicode_AS_UNICODE(wmode));
              :
    #endif
    if (NULL == f->f_fp && NULL != name) {
              :
      f->f_fp = fopen(name, newmode);
    
[/code]

Windows では `_wfopen()`、その他(Linux, OSX, FreeBSD, etc.)は `fopen()`
を使用しています。Linux の man で `fopen(3)` を参照すると、

> **fopen(3)**
>
> The mode string can also include the letter 'b' either as a last character
> or as a character between the characters in any of the two-character strings
> described above. This is strictly for compatibility with C89 and **has no
> effect** ; **the 'b' is ignored on all POSIX conforming systems, including
> Linux**. ( **Other systems may treat text files and binary files
> differently, and adding the 'b' may be a good idea if you do I/O to a binary
> file** and expect that your program may be ported to non-UNIX environments.)

POSIX 準拠の OS の場合、`fopen()` のモード指定に `b` オプションを付けても付けなくても同じ事になります。

一方、Windows ではどうでしょうか。

[MSDN: fopen、_wfopen](https://msdn.microsoft.com/ja-
jp/library/yeby3zcb\(v=vs.100\).aspx)

> b
>
> Open in binary (untranslated) mode; translations involving **carriage-return
> and linefeed characters are suppressed**.

また、`pickle.dump()` 関数を見てみますと、最終的に `cPickcle.c:write_file()`
関数でファイルに出力しているのですが、

**python2.7-2.7.10/Modules/cPickle.c:write_file() function**

[code]

    write_file(Picklerobject *self, const char *s, Py_ssize_t  n)
    {
      size_t nbyteswritten;
            :
      nbyteswritten = fwrite(s, sizeof(char), n, self->fp);
            :
    
[/code]

Windows 上でファイルをテキストモードでオープンした場合、`fwrite()` で `'\n' -> '\r\n'` 変換が発生することになります。

[MSDN: fwrite](https://msdn.microsoft.com/ja-jp/library/h9t88zwz.aspx)

> The fwrite function writes up to count items, of size length each, from
> buffer to the output stream. The file pointer associated with stream (if
> there is one) is incremented by the number of bytes actually written. **If
> stream is opened in text mode, each linefeed is replaced with a carriage-
> return – linefeed pair**.

Python が Windows を含むマルチプラットフォーム対応である事を鑑みれば、`pickle.dump()`
に渡すファイル(ポインタ)はバイナリモードでオープンしておいた方が良いかと思います。


CC BY-SA 3.0
2015-11-18T14:21:03.433
18999
2015-11-18T14:21:03.433
18992
answer