首页 / Python3 入门教程 / StringIO 与 BytesIO

Python3 入门教程

StringIO 与 BytesIO

本教程共 70 篇 · 第 46 篇 · 更新于 2026-07-22 · 约 4 分钟阅读

PythonPython3 入门教程StringIOBytesIO内存文件io模块

46. StringIO 与 BytesIO

本节目标:理解内存文件的概念,学会用 StringIOBytesIO 在内存中读写数据,掌握它们的典型使用场景。

很多时候你需要「像操作文件一样读写数据」,但数据其实不需要存到磁盘上。比如在单元测试里模拟文件内容,或者在网络程序里暂存收到的数据。

StringIOBytesIO 提供了内存中的「伪文件」对象。它们有和真实文件一样的 read()write() 方法,但数据完全存在于内存中。

StringIO:内存中的文本文件

from io import StringIO

# 写入
output = StringIO()
output.write('第一行\n')
output.write('第二行\n')

# 获取全部内容
content = output.getvalue()
print(content)

output.close()

StringIO 的行为和文本模式的文件对象几乎一样:

from io import StringIO

# 用字符串初始化
input_stream = StringIO('Hello\nWorld\n')

for line in input_stream:
    print(line.strip())

input_stream.close()
Tip

StringIO 处理的是 str(文本),不是 bytes。如果你需要处理二进制数据,用 BytesIO

BytesIO:内存中的二进制文件

from io import BytesIO

# 写入二进制数据
output = BytesIO()
output.write(b'\x89PNG\r\n\x1a\n')

# 获取 bytes
data = output.getvalue()
print(data)

output.close()

BytesIOStringIO 的 API 完全一致,区别只在于前者操作 bytes,后者操作 str

为什么不用普通字符串/列表

你可能会问:直接在字符串上拼接不就行了吗?为什么需要 StringIO

原因有两点:

  1. 接口统一:很多函数接受「文件对象」作为参数。用 StringIO 可以让这些函数处理内存数据,不需要改动接口。
  2. 性能:频繁字符串拼接会创建大量临时对象。StringIO 内部用列表缓冲,最后一次性拼接,效率更高。
# 反例:频繁拼接
result = ''
for i in range(10000):
    result += f"line {i}\n"

# 正例:用 StringIO
from io import StringIO
buf = StringIO()
for i in range(10000):
    buf.write(f"line {i}\n")
result = buf.getvalue()
Note

对于超大规模数据,直接用列表 appendjoin 性能最好。StringIO 的优势在于「兼容文件接口」,不只是性能。

典型场景

场景一:单元测试模拟文件

测试一个解析 CSV 的函数,不需要真的创建临时文件:

from io import StringIO

def parse_csv(file_obj):
    result = []
    for line in file_obj:
        result.append(line.strip().split(','))
    return result

# 测试
csv_data = "name,age\nAlice,25\nBob,30\n"
fake_file = StringIO(csv_data)
result = parse_csv(fake_file)

assert result[1] == ['Alice', '25']

场景二:捕获 print 输出

验证某个函数是否正确打印了日志:

from io import StringIO
import sys

def log_message(msg):
    print(f"[LOG] {msg}")

# 重定向 stdout
old_stdout = sys.stdout
sys.stdout = captured = StringIO()

log_message("系统启动")

sys.stdout = old_stdout
output = captured.getvalue()
assert "系统启动" in output
Warning

重定向 sys.stdout 是全局操作,多线程环境下会有竞态问题。更安全的做法是给被测函数加一个 file 参数,让它支持传入任意文件对象。

场景三:网络数据暂存

收到一段二进制数据,需要像读文件一样解析:

from io import BytesIO

def parse_header(data: bytes):
    stream = BytesIO(data)
    magic = stream.read(4)
    version = int.from_bytes(stream.read(2), 'big')
    return magic, version

raw = b'\x89PNG\r\n\x1a\n\x00\x01'
magic, version = parse_header(raw)
print(magic, version)

场景四:图片处理中间缓冲

from io import BytesIO
from PIL import Image

# 把内存中的 bytes 当成图片打开
img_data = download_image()  # 假设返回图片的 bytes
img = Image.open(BytesIO(img_data))
img.thumbnail((100, 100))

# 处理后写回内存
output = BytesIO()
img.save(output, format='PNG')
processed_data = output.getvalue()

与 with 语句配合

StringIOBytesIO 也支持上下文管理器:

from io import StringIO

with StringIO() as buf:
    buf.write('line 1\n')
    buf.write('line 2\n')
    content = buf.getvalue()

print(content)

不过它们没有真正的系统资源需要释放,close() 只是释放内部缓冲区。用不用 with 差别不大,但保持统一风格总是好的。

小结

  • StringIO 在内存中模拟文本文件,BytesIO 模拟二进制文件。
  • 核心方法:write()read()getvalue()seek()tell()
  • 主要价值是「兼容文件接口」和「避免频繁字符串拼接」。
  • 单元测试、网络数据解析、图片处理是典型应用场景。

磁盘 I/O 是程序里最慢的操作之一。把数据留在内存里处理,能显著提升性能。StringIOBytesIO 就是让你在不改动「文件式」代码的前提下,享受内存的速度。


来源:参考了 runoob「Python StringIO 模块」、liaoxuefeng「StringIO 和 BytesIO」等,改写后所得。