# 18.10. `multifile` --- 個別の部分を含んだファイル群のサポート

バージョン 2.5 で非推奨: `multifile` モジュールよりも `email` パッケージを使うべきです。このモジュールは後方互換性のためだけに存在しています。

`MultiFile` オブジェクトは、テキストファイルのセクション分割をファイル類似の入力オブジェクトのように扱えるようにします。指定したデリミタのパターンに遭遇すると `readline()` が `''` を返します。このクラスの標準設定は MIME マルチパートメッセージを解釈する上で便利となるように設計されていますが、サブクラス化を行って幾つかのメソッドを上書きすることで、簡単に汎用目的に対応させることができます。

class `multifile.``MultiFile`(fp[, seekable])

  マルチファイルを生成します。fp 引数には、例えば `open()` が返すファイルオブジェクトのような、`MultiFile` インスタンスが行データを取得出来るオブジェクトを渡す必要があります。

  `MultiFile` は入力オブジェクトの `readline()` 、 `seek()` 、および `tell()` メソッドしか参照せず、後者の二つのメソッドは個々の MIME パートにランダムアクセスしたい場合にのみ必要です。 `MultiFile` を seek できないストリームオブジェクトで使うには、オプションの seekable 引数の値を偽にしてください; これにより、入力オブジェクトの `seek()` および `tell()` メソッドを使わないようになります。

`MultiFile` の視点から見ると、テキストは三種類の行データ: データ、セクション分割子、終了マーカ、からなることを知っていると役に立つでしょう。 MultiFile は、多重入れ子構造になっている可能性のある、それぞれが独自のセクション分割子および終了マーカのパターンを持つメッセージパートをサポートするように設計されています。

参考

`email` モジュール
  包括的な e-mail 処理パッケージです。 `multifile` に取って代わります。

## 18.10.1. MultiFile オブジェクト

`MultiFile` インスタンスは以下のメソッドを持っています:

`MultiFile.``readline`(str)

  一行データを読みます。その行が (セクション分割子や終了マーカや本物の EOF でない) データの場合、行データを返します。その行がもっとも最近スタックにプッシュされた境界パターンにマッチした場合、 `''` を返し、マッチした内容が終了マーカかそうでないかによって `self.last` を 1 か 0 に設定します。行がその他のスタックされている境界パターンにマッチした場合、エラーが送出されます。背後のストリームオブジェクトにおけるファイルの終端に到達した場合、全ての境界がスタックから除去されていない限りこのメソッドは `Error` を送出します。

`MultiFile.``readlines`(str)

  このパートの残りの全ての行を文字列のリストとして返します。

`MultiFile.``read`()

  次のセクションまでの全ての行を読みます。読んだ内容を単一の (複数行にわたる) 文字列として返します。このメソッドには size 引数をとらないので注意してください!

`MultiFile.``seek`(pos[, whence])

  ファイルを seek します。 seek する際のインデクスは現在のセクションの開始位置からの相対位置になります。 pos および whence 引数はファイルの seek における引数と同じように解釈されます。

`MultiFile.``tell`()

  現在のセクションの先頭に対して相対的なファイル位置を返します。

`MultiFile.``next`()

  次のセクションまで行を読み飛ばします (すなわち、セクション分割子または終了マーカが消費されるまで行データを読みます)。次のセクションがあった場合には真を、終了マーカが発見された場合には偽を返します。最も最近スタックにプッシュされた境界パターンを最有効化します。

`MultiFile.``is_data`(str)

  str がデータの場合に真を返し、セクション分割子の可能性がある場合には偽を返します。このメソッドは行の先頭が (全ての MIME 境界が持っている) `'-``-'` 以外になっているかを調べるように実装されていますが、派生クラスで上書きできるように宣言されています。

  このテストは実際の境界テストにおいて高速性を保つために使われているので注意してください; このテストが常に false を返す場合、テストが失敗するのではなく、単に処理が遅くなるだけです。

`MultiFile.``push`(str)

  境界文字列をスタックにプッシュします。この境界文字列の修飾されたバージョンが入力行に見つかった場合、セクション分割子または終了マーカであると解釈されます(どちらであるかは修飾に依存します。 RFC 2045 を参照してください)。それ以降の全てのデータ読み出しは、 `pop()` を呼んで境界文字列を除去するか、 `next()` を呼んで境界文字列を再有効化しないかぎり、ファイル終端を示す空文字列を返します。

  一つ以上の境界をプッシュすることは可能です。もっとも最近プッシュされた境界に遭遇すると EOF が返ります; その他の境界に遭遇するとエラーが送出されます。

`MultiFile.``pop`()

  セクション境界をポップします。この境界はもはや EOF として解釈されません。

`MultiFile.``section_divider`(str)

  境界をセクション分割子にします。標準では、このメソッドは (全ての MIME 境界が持っている) `'--'` を境界文字列の先頭に追加しますが、これは派生クラスで上書きできるように宣言されています。末尾の空白は無視されることから考えて、このメソッドでは LF や CR-LF を追加する必要はありません。

`MultiFile.``end_marker`(str)

  境界文字列を終了マーカ行にします。標準では、このメソッドは (MIME マルチパートデータのメッセージ終了マーカのように) `'--'` を境界文字列の先頭に追加し、かつ `'--'` を境界文字列の末尾に追加しますが、これは派生クラスで上書きできるように宣言されています。末尾の空白は無視されることから考えて、このメソッドでは LF や CR-LF を追加する必要はありません。

最後に、 `MultiFile` インスタンスは二つの公開されたインスタンス変数を持っています:

`MultiFile.``level`

  現在のパートにおける入れ子の深さです。

`MultiFile.``last`

  最後に見つかったファイル終了イベントがメッセージ終了マーカであった場合に真となります。

## 18.10.2. `MultiFile` の例

    import mimetools
    import multifile
    import StringIO

    def extract_mime_part_matching(stream, mimetype):
        """Return the first element in a multipart MIME message on stream
        matching mimetype."""

        msg = mimetools.Message(stream)
        msgtype = msg.gettype()
        params = msg.getplist()

        data = StringIO.StringIO()
        if msgtype[:10] == "multipart/":

            file = multifile.MultiFile(stream)
            file.push(msg.getparam("boundary"))
            while file.next():
                submsg = mimetools.Message(file)
                try:
                    data = StringIO.StringIO()
                    mimetools.decode(file, data, submsg.getencoding())
                except ValueError:
                    continue
                if submsg.gettype() == mimetype:
                    break
            file.pop()
        return data.getvalue()