2012-08-09 10 views
32

ファイル名を引数とし、ファイルの内容を処理する従来の関数を持つレガシーコードがあります。コードの作業ファクシミリは以下の通りです。StringIOと 'with'ステートメント(コンテキストマネージャ)との互換性

このレガシー機能を使用するために生成するコンテンツをディスクに書き込む必要はありません。したがって、物理ファイル名の代わりにStringIOを使用してオブジェクトを作成することはできます。しかし、これは以下のように動作しません。

私はStringIOと思っていました。誰も私にこのレガシー関数を使用し、ディスク上のファイルではないが、レガシー関数で扱うことができる引数に何かを渡す方法があるかどうか教えていただけますか?従来の関数では、パラメータ値に対して作業を行うwithコンテキストマネージャがあります。

私がいたグーグルで出会った一つのこと:http://bugs.python.org/issue1286、それが私を助けにはならなかった...

コード

from pprint import pprint 
import StringIO 

    # Legacy Function 
def processFile(filename): 
    with open(filename, 'r') as fh: 
     return fh.readlines() 

    # This works 
print 'This is the output of FileOnDisk.txt' 
pprint(processFile('c:/temp/FileOnDisk.txt')) 
print 

    # This fails 
plink_data = StringIO.StringIO('StringIO data.') 
print 'This is the error.' 
pprint(processFile(plink_data)) 

出力

これは、 FileOnDisk.txtの出力:

['This file is on disk.\n'] 

これはエラーです:

Traceback (most recent call last): 
    File "C:\temp\test.py", line 20, in <module> 
    pprint(processFile(plink_data)) 
    File "C:\temp\test.py", line 6, in processFile 
    with open(filename, 'r') as fh: 
TypeError: coercing to Unicode: need string or buffer, instance found 
+3

あなたが「オープン」、それは内部でなければならないので、問題が解決しない残念なことにたStringIOインスタンス –

答えて

52

StringIOインスタンスはすでに開いているファイルです。一方、openコマンドは、開いているファイルを返すためにファイル名のみをとります。 StringIOインスタンスはファイル名としては適していません。

また、StringIOインスタンスを閉じる必要はないため、コンテキストマネージャとしても使用する必要はありません。

すべての従来のコードで使用できるファイル名がある場合は、StringIOインスタンスは使用できません。代わりにtempfile moduleを使用して一時ファイル名を生成してください。ここで

は、一時ファイルはその後クリーンアップされていることを確認するためにcontextmanagerを使った例です:

import os 
import tempfile 
from contextlib import contextmanager 

@contextmanager 
def tempinput(data): 
    temp = tempfile.NamedTemporaryFile(delete=False) 
    temp.write(data) 
    temp.close() 
    try: 
     yield temp.name 
    finally: 
     os.unlink(temp.name) 

with tempinput('Some data.\nSome more data.') as tempfilename: 
    processFile(tempfilename) 
+0

私はこのソリューションを使用しています。ここに、これを直接実装するサンプルコードへのリンクがあります:http://pastie.org/4450354。ここに貢献した皆様、ありがとう! – mpettis

+2

@mpettis:一時ファイルを作成して一度にクリーンアップするコンテキストマネージャを使って例を挙げて答えを更新しました。 –

+0

これは実際にこれを処理するエレガントな方法です...ありがとう! – mpettis

4

あなたはそのが行われた後__exit__呼び出すしたいとのStringIOはexitメソッドを持っていない...

でしかし

fopen = open 
def open(fname,mode): 
    if hasattr(fname,"readlines"): return fname 
    else: return fopen(fname,mode) 

独自のオープン関数を定義することができます

このオープンで使用するカスタムクラスを定義できます

class MyStringIO: 
    def __init__(self,txt): 
     self.text = txt 
    def readlines(self): 
      return self.text.splitlines() 
    def __exit__(self): 
      pass 
+0

をカントレガシーファンクション – jdi

+0

は、同じファイル内にある限り、このオープンを上書きしませんか? –

+0

@jdiレガシー機能の前、つまりレガシーモジュールがインポートされる前に定義されているとうまくいくと思います。 –