python中的StringIO实际上用于什么?


75

我不是专业人士,我一直在努力了解StringIO的确切用途。我一直在互联网上找一些例子。但是,几乎所有示例都是非常抽象的。他们只是展示“如何”使用它。但是它们都没有显示“为什么”和“在什么情况下”应该/将要使用它?提前致谢

ps不要与stackoverflow上的这个问题混淆:StringIO用法,用于比较string和StringIo。

Answers:


90

当您有一些仅接收文件的API,但需要使用字符串时,可以使用它。例如,要使用Python 2中的gzip模块压缩字符串:

import gzip
import StringIO

stringio = StringIO.StringIO()
gzip_file = gzip.GzipFile(fileobj=stringio, mode='w')
gzip_file.write('Hello World')
gzip_file.close()

stringio.getvalue()

2
换句话说::duck typingD
Abdelouahab 2014年

2
从Python 3.2开始,gzip模块具有直接压缩数据的功能。(但是,当前需要StringIO的任何知名开源库都可能会在一段时间后增加此类功能,因此,我不再在这里搜索gzip而不是寻找新的示例。)
Petr Viktorin

36

StringIO使您可以像文件一样访问字符串,因此您可以使用处理文件的现有模块,几乎不做任何更改即可使它与字符串一起使用。

例如,假设您有一个将文件写入文件的记录器,而您想通过网络发送日志输出。您可以读取文件并将其内容写入网络,也可以将日志写入StringIO对象,然后将其发送到其网络目标,而无需接触文件系统。使用StringIO可以很容易地通过第一种方法进行操作,然后切换到第二种方法。


1
stringIO还有助于将文件直接写入S3(即,无需先保存本地然后上传)。
7bStan

17

如果您想要一个像文件一样的文件对象,但又要写入内存中的字符串缓冲区:StringIO是工具。如果您要构建大型字符串(例如纯文本文档)并进行大量字符串连接,则可能会发现,仅使用StringIO而不是一堆mystr += 'more stuff\n'类型的操作会更容易。


3
我还发现,StringIO是相当快,如果你正在处理的字符数据的多个兆字节相比,像表达式时,mystr += "more stuff\n"在一个循环中,特别是如果你可以使用cStringIO.StringIO,而不是只io.StringIO
很少“莫妮卡在哪里”有需要的人

@SeldomNeedy你基准了吗?在2016年可能确实如此,但是如今使用+ =进行的字符串连接已得到了优化(在安全的情况下,它使用引用计数器将字符串更改为适当的位置)。基准:$ python3 -m timeit -s "from io import StringIO; line = 'a'*80" $'s = StringIO()\nfor i in range(10000): s.write(line)\ns = s.getvalue()'500 loops, best of 5: 599 usec per loop; python3 -m timeit -s "line = 'a'*80" $'s = ""\nfor i in range(10000): s += line'500 loops, best of 5: 588 usec per loop
克莱门特

@Clément我指的是Python 2.x;cStringIO甚至在Python 3中也不存在。很高兴看到天真的实现在3.x中得到了优化!
很少“莫妮卡在哪里”有需要的人

@SeldomNeedy+=在AFAICT 2和Python 2中也得到了很好的优化:+=基准测试的版本是StringIO的十倍,是Python2中StringIO的三倍。(此外,您的帖子中提到了io.StringIO;不是仅Python 3吗?)
Clément20年


10

我个人已将其用于以下几点:

  1. 整个文件缓存。我有一个脚本,可读取PDF并验证有关它们的各种信息。我正在使用的PDF库在其文档构造函数中使用一个打开的文件。我最初只是打开了我感兴趣阅读的PDF,但是当我更改它以立即将整个文件读取到内存中,然后将StringIO对象传递给PDF库时,脚本的运行时间减少了一半。

  2. 延迟打印。同一脚本在读取每个PDF之前都会打印一个标题。但是,我可以在命令行上指定是忽略其配置文件中的某些测试,还是仅包括某些测试。如果我忽略给定PDF的所有测试,则不希望打印标题,但在运行完测试之前,我不知道要运行多少测试(也可以动态定义测试)。因此,我通过更改标题将其捕获到StringIO对象中,sys.stdout并指向它,每次运行测试时,我都会检查该对象中是否包含任何内容。如果是这样,我将其打印出来并将其重置为空。瞧,只有经过测试的PDF才会打印标题。


9

我刚刚在实践中使用StringIO做两件事:

  • print通过重定向sys.stdout到一个StringIO实例以便于分析,可以对一个可以完成很多工作的脚本进行单元测试;
  • 要使用创建一个有保证的格式正确的XML文档(自定义API请求)ElementTree,然后write通过HTTP连接进行发送。

并不是您StringIO 经常需要,但有时它很有用。


7

我已经用它代替了文本文件进行单元测试。

例如,要制作csv“文件”以进行熊猫测试(Python 3):

import io
f = io.StringIO("id,name\n1,brian\n2,amanda\n3,zoey\n")
df = pd.read_csv(f) # pandas takes a file path or a file-like object

这里的文档:

文本I / O的内存流。调用close()方法时,将丢弃文本缓冲区。

可以通过提供initial_value来设置缓冲区的初始值。

方法getvalue():返回包含缓冲区全部内容的str。


1

Django具有call_command用于调用管理命令的功能。此函数将输出打印到stdout,并且不返回任何值。如果您想知道命令是否成功运行,则必须查看输出并做出决定。

使用StringIO,您可以捕获输出并检查是否需要输出。

with io.StringIO() as output:
    call_command('custom_command', stdout=output)
    if 'Success' not in output.getvalue():
        print('Custom command failed...')

0

这是StringIO用例的具体示例:直接将一些数据写入AWS s3,而无需在本地磁盘上创建文件:

import csv
import io
import boto3

data = [
    ["test", "data", "headers etc", "123","",],
    ["blah", "123", "35", "blah","",],
    ["abc", "def", "blah", "yep", "blah"]
]

bucket_name = 'bucket_name_here'
session = boto3.Session(
    aws_access_key_id = "fake Access ID"),
    aws_secret_access_key = "fake Secret key"),
    region_name = "ap-southeast-2")
)
s3 = session.resource('s3')
with io.StringIO() as f:
    writer = csv.writer(f, delimiter=",")
    writer.writerows(data)
    resp = s3.Object(bucket_name, "test.csv").put(Body=f.getvalue())

无需在本地磁盘上写入任何内容,即可享受S3上的新csv!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.