2012-08-09 32 views
32

我有一些遺留代碼與傳統函數,將文件名作爲參數並處理文件內容。代碼的工作傳真如下。StringIO與'與'語句的兼容性(上下文管理器)

我想要做的就是不必爲了使用這個遺留功能而生成的一些內容寫入磁盤,所以我可以使用StringIO來創建一個對象來代替物理文件名。但是,這不起作用,如下所示。我認爲StringIO是這樣的方式。任何人都可以告訴我,如果有一種方法可以使用這個遺留函數,並將它傳遞給不是磁盤上文件的參數,而是可以通過遺留函數來處理?舊功能的with上下文管理器正在處理filename參數值。

我在谷歌遇到的一兩件事是: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的實例 – 2012-08-09 22:07:15

回答

52

A StringIO實例已打開文件。另一方面,open命令僅使用文件名來返回打開的文件。 A 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 2012-08-10 15:49:41

+2

@mpettis:我已經更新了我的答案,給出了一個使用上下文管理器的例子,它將創建臨時文件並一次性清理它。 – 2012-08-10 15:56:53

+0

這真是一個優雅的方式來處理這個......謝謝! – mpettis 2012-08-10 20:17:10

4

你可以定義自己的開放功能

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

然而與要調用__exit__其完成和StringIO的沒有退出方法之後...

您可以定義一個自定義類以便在此打開時使用

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

不幸的是這並沒有解決問題,因爲它會在裏面遺留函數 – jdi 2012-08-09 22:10:40

+0

將不會覆蓋它,只要它在同一個文件中? – 2012-08-09 22:13:04

+0

@jdi我認爲它可能工作,如果它是在傳統功能之前定義的,即傳統模塊被導入。然而, – 2012-08-09 22:14:33