【问题标题】:What is StringIO in python used for in reality?python中的StringIO在现实中是做什么用的?
【发布时间】:2011-12-21 05:47:31
【问题描述】:

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

附言不要与 stackoverflow 上的这个问题混淆:StringIO Usage 比较字符串和 StringIo。

【问题讨论】:

    标签: python string caching io


    【解决方案1】:

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

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

    【讨论】:

    • stringIO 也有助于将文件直接写入 S3(即无需先在本地保存然后上传)。
    【解决方案2】:

    当你有一些只接受文件的 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()
    

    【讨论】:

    • 换句话说:duck typing:D
    • 从 Python 3.2 开始,gzip 模块具有直接压缩数据的功能。 (但任何目前需要 StringIO 的知名开源库都可能会在一段时间后增加此类功能,所以与其寻找新的示例,我将把 gzip 留在这里。)
    【解决方案3】:

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

    【讨论】:

    • 我还发现 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 looppython3 -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 += 在 Python 2 中也得到了很好的优化,AFAICT:上面的基准测试的+= 版本是 Python2 中 StringIO 的 10 倍和 StringIO 的 3 倍. (另外,你的帖子提到了io.StringIO;那不是只有 Python 3 吗?)
    【解决方案4】:

    我个人用它做的几件事:

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

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

    【讨论】:

      【解决方案5】:

      我刚刚在实践中将 StringIO 用于两件事:

      • 通过将sys.stdout 重定向到StringIO 实例以方便分析,对执行大量printing 的脚本进行单元测试;
      • 使用ElementTreewrite 创建有保证的格式良好的 XML 文档(自定义 API 请求),以便通过 HTTP 连接发送。

      不是你需要StringIO经常,但有时它非常有用。

      【讨论】:

        【解决方案6】:

        Django 有一个函数call_command,用于调用管理命令。此函数将输出打印到标准输出并且不返回任何值。如果你想知道命令是否成功运行,你必须查看输出并决定。

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

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

        【讨论】:

          【解决方案7】:

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

          例如,制作一个 csv“文件”以使用 pandas (Python 3) 进行测试:

          import io
          import pandas as pd
          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
          

          来自文档here

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

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

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

          【讨论】:

            【解决方案8】:

            下面是 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!

            【讨论】:

              猜你喜欢
              • 2019-09-17
              • 1970-01-01
              • 2012-01-19
              • 2015-11-19
              • 2022-12-20
              • 2012-04-28
              • 2020-04-30
              • 2011-12-20
              • 2012-01-27
              相关资源
              最近更新 更多