如何在Python3中将UTF-8 CSV写入BytesIO?

6

首先,我了解如何在Python3中从字符串编写UTF-8,并且建议使用StringIO进行此类字符串构建。但是,我需要一个特定的二进制文件对象,因此我需要使用BytesIO。如果我按照以下方式进行,则数据最终会膨胀,因为它被读取为Latin1,这是我的电脑默认的区域设置/字符集。

with io.StringIO() as sb:
    csv.writer(sb).writerows(rows)
    sb.flush()
    sb.seek(0)
    # blows up with Latin1 encoding error
    job = bq.load_table_from_file(sb, table_ref, job_config=job_config)

因此,我的解决方法是这个怪物,它会使内存使用量增加一倍:

with io.StringIO() as sb:
    csv.writer(sb).writerows(rows)
    sb.flush()
    sb.seek(0)
    with io.BytesIO(sb.getvalue().encode('utf-8')) as buffer:
        job = bq.load_table_from_file(buffer, table_ref, job_config=job_config)

在这个过程中,一定要有一种方法来指定字节编码方式,以便于读取文件类似的sb数据时将其视为UTF-8。还是说有没有一种方法可以使用csv.writer()与字节流一起使用?
我在StackOverflow上寻找了这两个答案,但我所找到的通常是关于写入文件和内存中的东西,而所有的指向都是StringIO

必须有一种方法可以直接从“行”中创建作业。否则,整个过程不仅在内存使用方面效率低下,而且在 CPU 使用方面也效率低下,因为将字符编码为字节再转换回来的成本相当高昂。 - aventurin
创建一个任务需要联系API服务,这意味着“行”必须被序列化为传输格式,例如JSON或CSV。 - Neil C. Obremski
1个回答

6

有一个名为TextIOWrapper的类可以实现这个功能,但如果你使用上下文管理器with,它将会关闭流并使原始的BytesIO对象无法使用。

修改我的原始示例:

with io.BytesIO() as buffer:
    sb = io.TextIOWrapper(buffer, 'utf-8', newline='')
    csv.writer(sb).writerows(rows)
    sb.flush()
    buffer.seek(0)
    job = bq.load_table_from_file(buffer, table_ref, job_config=job_config)

另一个需要注意的是newline参数,如果不作处理会翻译换行符。将newline = ''设置为防止这种情况发生。


我认为关闭流的不是上下文管理器,而是当csv写入器超出范围时 - 请参见https://dev59.com/lqjja4cB1Zd3GeqP_n_q#48434568 - David Waterworth
1
TextIOWrapper 实际上在 超出范围时关闭流(csv.writer 不会关闭流)。有一个 detach() 方法可以避免这种行为 - 这是一种不幸的设计,需要保存对 TextIOWrapper 的引用,即使你只需要将字符串暂时传递到字节。 - Neil C. Obremski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接