53

我有一些带有遗留函数的遗留代码,该函数将文件名作为参数并处理文件内容。代码的工作传真如下。

我想要做的不是必须将我生成的一些内容写入磁盘才能使用这个遗留功能,所以我虽然可以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
4

3 回答 3

75

StringIO实例已经一个打开的文件。open另一方面,该命令只接受文件名,返回一个打开的文件。StringIO实例不适合作为文件名。

此外,您不需要关闭StringIO实例,因此也不需要将其用作上下文管理器。虽然关闭实例会释放分配的内存,但让垃圾收集器获取对象也是如此。无论如何,如果您想确保释放内存同时仍持有对对象的引用,contextlib.closing()上下文管理器可以负责关闭对象。

如果您的所有遗留代码都可以采用文件名,那么StringIO实例不是要走的路。改用该tempfile模块生成一个临时文件名。

这是一个使用上下文管理器来确保临时文件随后被清理的示例:

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)

您还可以切换到io模块提供的更新的 Python 3 基础设施(在 Python 2 和 3 中可用),其中/io.BytesIO是更强大的替代品。该对象确实支持用作上下文管理器(但仍不能传递给)。StringIO.StringIOcStringIO.StringIOopen()

于 2012-08-09T22:10:49.883 回答
6

你可以定义你自己的开放函数

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
于 2012-08-09T22:05:45.040 回答
2

这个是基于contextmanager的python doc

它只是用简单的上下文包装了StringIO,当exit被调用时,它会返回到屈服点,并正确关闭StringIO。这避免了制作临时文件的需要,但是对于大字符串,这仍然会占用内存,因为 StringIO 缓冲了该字符串。它适用于大多数您知道字符串数据不会很长的情况

from contextlib import contextmanager

@contextmanager
def buildStringIO(strData):
    from cStringIO import StringIO
    try:
        fi = StringIO(strData)
        yield fi
    finally:
        fi.close()

然后你可以这样做:

with buildStringIO('foobar') as f:
    print(f.read()) # will print 'foobar'
于 2018-09-20T08:20:14.190 回答