#
7. 入力と出力

プログラムの出力をもたらす方法はいくつかあります; データは人間が可読 な形で出力することも、将来使うためにファイルに書くこともできます。 この章では、こうした出力のいくつかの可能性について議論します。

#
7.1 ファンシーな出力の書式化

これまでのところ、値を出力する二つの方法: 式でできた文 (expression statement) と `print` 文が出てきました。(第三は ファイルオブジェクトの `write()` を使う方法です; 標準出力 を表すファイルは `sys.stdout` で参照できます。詳細は ライブラリリファレンスを参照してください。)

出力を書式化する際に、単に値をスペースで区切って出力するよりも もっときめ細かな制御をしたいと思うことがしばしばあるでしょう。 出力を書式化するには二つの方法があります; 第一の方法は、全ての 文字列を自分で処理するというものです; 文字列のスライスや結合といった操作を 使えば、思い通りのレイアウトを作成することができます。 標準モジュール `string` には、 文字列を指定されたカラム幅にそろえるための便利な操作がいくつか あります; これらの操作ついては、後で簡単に説明します。 第二の方法は `%` 演算子を使い、文字列を演算子の左引数 (left argument) として使う方法です。`%` 演算子は、左引数を `sprintf()` のような形式で解釈して右引数に適用し、 その書式化操作で得られた文字列を返します。

もちろん、一つ問題があります。値をどうやって文字列に変換したら いいのでしょうか？幸運なことに、Python には値を文字列に変換する方法が あります: 値を `repr()` か `str()` 関数に渡して ください。逆クオート (````) は `repr()` と等しい 操作ですが、利用はお勧めしません。

`str()` 関数は、値を表現するときにかなり人間にとって可読な ものにするためのものです。一方、`repr()` は インタプリタで読めるような表現にする (あるいは、等価な値を表現する ための構文がない場合には `SyntaxError` を送出させる) ためのものです。 人間が利用するための特別な表現をもたないオブジェクトでは、 `str()` は `repr()` と同じ値を返します。 数値や、リストや辞書といった構造体のような多くの値は、どちらの関数でも 同じ表現になります。文字列と浮動小数点は特別で、二つの 別個の表現となります。

下にいくつか例を挙げます:

    >>> s = 'Hello, world.'
    >>> str(s)
    'Hello, world.'
    >>> repr(s)
    "'Hello, world.'"
    >>> str(0.1)
    '0.1'
    >>> repr(0.1)
    '0.10000000000000001'
    >>> x = 10 * 3.25
    >>> y = 200 * 200
    >>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
    >>> print s
    The value of x is 32.5, and y is 40000...
    >>> # 文字列への repr() はクォートとバックスラッシュが付加される:
    ... hello = 'hello, world\n'
    >>> hellos = repr(hello)
    >>> print hellos
    'hello, world\n'
    >>> # repr() の引数は Python オブジェクトの場合もある:
    ... repr((x, y, ('spam', 'eggs')))
    "(32.5, 40000, ('spam', 'eggs'))"
    >>> # 逆クォートは対話セッショで便利である:
    ... `x, y, ('spam', 'eggs')`
    "(32.5, 40000, ('spam', 'eggs'))"

以下に 2 乗と 3 乗の値からなる表を書く二つの方法を示します:

    >>> for x in range(1, 11):
    ...     print repr(x).rjust(2), repr(x*x).rjust(3),
    ...     # 上の行の末尾のコンマに注意
    ...     print repr(x*x*x).rjust(4)
    ...
     1   1    1
     2   4    8
     3   9   27
     4  16   64
     5  25  125
     6  36  216
     7  49  343
     8  64  512
     9  81  729
    10 100 1000
    >>> for x in range(1,11):
    ...     print '%2d %3d %4d' % (x, x*x, x*x*x)
    ...
     1   1    1
     2   4    8
     3   9   27
     4  16   64
     5  25  125
     6  36  216
     7  49  343
     8  64  512
     9  81  729
    10 100 1000

(各カラムの間のスペース一個は `print` の働きで追加されて いることに注意してください: `print` は引数間に常に 空白を追加します)

この例では、メソッド `rjust()` を実際に利用しています。 `rjust()` は文字列を指定された幅のフィールド内に 右詰めで入るように、左に空白を追加します。同様のメソッドとして、 `ljust()` と `center()` が あります。これらのメソッドは何か出力を行うわけではなく、ただ新しい文字列を 返します。入力文字列が長すぎる場合、文字列を切り詰めることはせず、 ただ値をそのまま返します; この仕様のために、カラムのレイアウトが 滅茶苦茶になるかもしれませんが、嘘の値が代わりに書き出される よりはましです。(本当に切り詰めを行いたいのなら、全てのカラムに "`ljust(x, n)[0:n]`") のようにスライス表記を加える こともできます。)

もう一つのメソッド、 `zfill()` は、数値文字列 の左側をゼロ詰めします。このメソッドは正と負の符号を正しく扱います:

    >>> '12'.zfill(5)
    '00012'
    >>> '-3.14'.zfill(7)
    '-003.14'
    >>> '3.14159265359'.zfill(5)
    '3.14159265359'

`%` 演算子を使う場合は以下のようになります:

    >>> import math
    >>> print 'The value of PI is approximately %5.3f.' % math.pi
    The value of PI is approximately 3.142.

文字列の中に複数の書式がある場合には、以下の例のように、右側の被演算子 にタプルを渡す必要があります:

    >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
    >>> for name, phone in table.items():
    ...     print '%-10s ==> %10d' % (name, phone)
    ...
    Jack       ==>       4098
    Dcab       ==>       7678
    Sjoerd     ==>       4127

ほとんどの書式化は C 言語と同じように動作し、正しい型を渡す 必要があります; しかし、正しい型を渡さなかった場合にはコアダンプ ではなく例外の送出になります。書式 `%s` はもっと寛大です: 対応する引数が文字列オブジェクトでなければ、組込み関数 `str()` を使って文字列に変換してくれます。また、数値表現の桁幅や精度を別個の (整数の) 引数として渡せるよう、`*` がサポートされています。 C 言語の書式 `%n` と `%p` はサポートされていません。

もしも長い書式化文字列があり、それを分割したくない場合には、 変数を引数の位置ではなく、変数の名前で参照できるとよいでしょう。 以下の形式 `%(name)format` を使えば可能になります:

    >>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
    >>> print 'Jack: %(Jack)d; Sjoerd: %(Sjoerd)d; Dcab: %(Dcab)d' % table
    Jack: 4098; Sjoerd: 4127; Dcab: 8637678

全てのローカルな変数が入った辞書を返す、新たに紹介する組み込み関数 `vars()` と組み合わせると特に便利です。

#
7.2 ファイルを読み書きする

`open()` はファイルオブジェクトを 返します。 `open()` は、 "`open(filename, mode)`" のように二つの引数を伴って 呼び出されることがほとんどです。

    >>> f=open('/tmp/workfile', 'w')
    >>> print f
    <open file '/tmp/workfile', mode 'w' at 80a0960>

最初の引数はファイル名の入った文字列です。二つめの引数もまた文字列で、 ファイルをどのように使うかを示す数個の文字が入っています。 mode は、ファイルが読み出し専用なら `'r'` 、 書き込み専用 (同名の既存のファイルがあれば消去されます) なら `'w'` とします。`'a'` はファイルを追記用に開きます; ファイルに書き込まれた 内容は自動的にファイルの終端に追加されます。`'r+'` はファイルを読み 書き両用に開きます。mode 引数はオプションです; 省略された場合には `'r'` であると仮定します。

Windows や Macintosh では、mode に `'b'` を追加すると ファイルをバイナリモードで開きます。したがって、 `'rb'`, `'wb'`, `'r+b'` といったモードがあります。 Windows はテキストファイルとバイナリファイルを区別しています; テキストファイルでは、読み書きの際に行末文字が自動的に少し変更 されます。 この舞台裏でのファイルデータ変更は、ASCII でできたテキストファイル では差し支えないものですが、JPEG や .EXE ファイルのような バイナリデータは破損してしまうことになるでしょう。 こうしたファイルを読み書きする際にはバイナリモードを使うよう十分 注意してください。 (Macintosh では、テキストモードに対する 厳密な意味付けは、根底にある使用中の C 言語ライブラリに依存する ので注意してください。)

##
7.2.1 ファイルオブジェクトのメソッド

この節の以降の例は、`f` というファイルオブジェクトが既に 生成されているものと仮定します。

ファイルの内容を読み出すには、`f.read(size)` を呼び出します。 このメソッドはある量のデータを読み出して、文字列として返します。 size はオプションの数値引数です。size が省略されたり 負の数であった場合、ファイルの内容全てを読み出して返します; ただし、 ファイルがマシンのメモリの二倍の大きさもある場合にはどうなるか わかりません。 size が負でない数ならば、最大で size バイトを読み出して 返します。ファイルの終端にすでに達していた場合、`f.read()` は 空の文字列 (`""`) を返します。

    >>> f.read()
    'This is the entire file.\n'
    >>> f.read()
    ''

`f.readline()` はファイルから 1 行だけを読み取ります; 改行文字 (`\n`) は読み出された文字列の終端に残ります。 改行が省略されるのは、ファイルが改行で終わっていない場合の最終行 のみです。これは、戻り値があいまいでないようにするためです; `f.readline()` が空の文字列を返したら、ファイルの終端に 達したことが分かります。一方、空行は `'\n'` 、すなわち 改行 1 文字だけからなる文字列で表現されます。

    >>> f.readline()
    'This is the first line of the file.\n'
    >>> f.readline()
    'Second line of the file\n'
    >>> f.readline()
    ''

`f.readlines()` は、ファイルに入っているデータの全ての行からなる リストを返します。オプションのパラメタ sizehint が指定されて いれば、ファイルから指定されたバイト数を読み出し、さらに一行を完成 させるのに必要なだけを読み出して、読み出された行からなる リストを返します。このメソッドは巨大なファイルを行単位で効率的に 読み出すためによく使われます。未完成の行が返されることはありません。

    >>> f.readlines()
    ['This is the first line of the file.\n', 'Second line of the file\n']

`f.write(string)` は、 string の内容をファイルに 書き込み、`None` を返します。

    >>> f.write('This is a test\n')

`f.tell()` は、ファイルオブジェクトが指しているあるファイル中の 位置を示す整数を、ファイルの先頭からのバイト数で図った値で返します。 ファイルオブジェクトの位置を変更するには、"`f.seek(offset, from_what)`" を使います。ファイル位置は基準点 (reference point) にオフセット値 offset を足して計算されます; 参照点は from_what 引数で選びます。 from_what の値が 0 ならばファイルの先頭から測り、 1 ならば現在のファイル位置を使い、2 ならばファイルの終端を 参照点として使います。 from_what は省略することができ、デフォルトの値は 0 、すなわち 参照店としてファイルの先頭を使います。

    >>> f=open('/tmp/workfile', 'r+')
    >>> f.write('0123456789abcdef')
    >>> f.seek(5)     # ファイルの第6バイトへ行く
    >>> f.read(1)
    '5'
    >>> f.seek(-3, 2) # 終端から前へ第3バイトへ行く
    >>> f.read(1)
    'd'

ファイルが用済みになったら、`f.close()` を呼び出してファイルを 閉じ、ファイルを開くために取られていたシステム資源を解放します。 `f.close()` を呼び出した後、そのファイルオブジェクトを使おうと すると自動的に失敗します。

    >>> f.close()
    >>> f.read()
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    ValueError: I/O operation on closed file

ファイルオブジェクトには、他にも `isatty()` や `truncate()` といった、あまり使われないメソッドがあります; ファイルオブジェクトについての完全なガイドは、ライブラリリファレンスを 参照してください。

##
7.2.2 `pickle` モジュール

文字列をファイルに読み書きするのは簡単にできます。数値でもほんの わずかに苦労するくらいです。というのは、`read()` は文字列だけを 返すので、`'123'` のような文字列を受け取って、その数値 123 を返す `int()` のような関数に対して文字列を渡してやらなければ ならないからです。ところが、リストや辞書、クラスのインスタンスのように、 もっと複雑なデータ型を保存したいなら、事態はもっと複雑になります。

複雑なデータ型を保存するためのコードを利用者に毎回毎回書かせて デバッグさせる代わりに、Python では `pickle` という標準 モジュールを用意しています。`pickle` は驚くべきモジュールで、 ほとんどどんな Python オブジェクトも (ある形式の Python コード でさえも!) 受け取って文字列表現へ変換できます。 この変換過程は pickling (ピクルス (漬物) 化、以降 pickle 化) と呼ばれます。文字列表現からオブジェクトを再構成する操作は unpickling (逆 pickle 化)と呼びます。 pickle 化や逆 unpickle 化の間、オブジェクトを表現する文字列は ファイルやデータに保存したり、ネットワーク接続を介して離れたマシンに 送信したりできます。

オブジェクト `x` と、書込み用に開かれているファイルオブジェクト `f` があると仮定すると、オブジェクトを pickle 化する最も簡単な 方法は、たった一行のコードしか必要ありません:

    pickle.dump(x, f)

逆 pickle 化して再びオブジェクトに戻すには、 `f` を読取り用に開かれているファイル・オブジェクトと仮定して:

    x = pickle.load(f)

とします。

(逆 pickle 化にはいくつか変型があり、たくさんのオブジェクトを pickle 化 したり、 pickle 化されたデータをファイルに書きたくないときに使われます。 完全なドキュメントについては、 ライブラリリファレンス の `pickle` を調べてください。)

`pickle` は、Python のオブジェクトを保存できるようにし、 他のプログラムや、同じプログラムが将来起動されたときに再利用 できるようにする標準の方法です; 技術的な用語でいうと persistent (永続性) オブジェクトです。 `pickle` はとても広範に使われている ので、Python 拡張モジュールの多くの作者は、行列のような新たなデータ型が 正しく pickle 化/unpickle 化できるよう気をつけています。