io.StringIO and io.BytesIO¶
StringIO and BytesIO create in-memory file-like objects for text and binary data respectively. They are useful for testing, temporary buffers, and situations where file system I/O should be avoided.
Mental Model
StringIO and BytesIO are fake files that live in memory. They support the same .read(), .write(), and .seek() interface as real files, so any code that expects a file-like object works with them unchanged. This makes them perfect for testing I/O code without touching the filesystem.
StringIO for Text¶
In-Memory Text Buffer¶
```python from io import StringIO
buffer = StringIO() buffer.write("Hello ") buffer.write("World!")
contents = buffer.getvalue() print(contents) ```
Output:
Hello World!
Reading from StringIO¶
```python from io import StringIO
buffer = StringIO("line1\nline2\nline3")
for line in buffer: print(line.strip()) ```
Output:
line1
line2
line3
BytesIO for Binary¶
In-Memory Binary Buffer¶
```python from io import BytesIO
buffer = BytesIO() buffer.write(b"Hello ") buffer.write(b"World!")
contents = buffer.getvalue() print(contents) ```
Output:
b'Hello World!'
Binary Data Processing¶
```python from io import BytesIO
image_data = BytesIO() image_data.write(b'\x89PNG\r\n\x1a\n') image_data.write(b'mock_image_data')
image_data.seek(0) header = image_data.read(8) print(f"PNG Header: {header}") ```
Output:
PNG Header: b'\x89PNG\r\n\x1a\n'
Testing and Mocking¶
Testing File Operations¶
```python from io import StringIO import sys
buffer = StringIO() sys.stdout = buffer print("Captured output") sys.stdout = sys.stdout
result = buffer.getvalue() print(f"Result: {result}") ```
Output:
Result: Captured output
CSV Processing¶
```python from io import StringIO import csv
csv_data = StringIO() writer = csv.writer(csv_data) writer.writerow(['name', 'age']) writer.writerow(['Alice', 30]) writer.writerow(['Bob', 25])
csv_data.seek(0) reader = csv.reader(csv_data) for row in reader: print(row) ```
Output:
['name', 'age']
['Alice', '30']
['Bob', '25']
Exercises¶
Exercise 1.
Use io.StringIO to capture the output of multiple print() calls into a single string. Print the combined result.
Solution to Exercise 1
```python
import io
buffer = io.StringIO()
print("Hello", file=buffer)
print("World", file=buffer)
print("Python", file=buffer)
result = buffer.getvalue()
print(result)
# Hello
# World
# Python
```
io.StringIO acts as an in-memory text file. print() with file=buffer writes to it instead of stdout.
Exercise 2.
Write a function that takes a CSV string (e.g., "name,age\nAlice,30\nBob,25") and uses io.StringIO with the csv module to parse it into a list of dictionaries.
Solution to Exercise 2
```python
import io
import csv
def parse_csv(csv_string):
reader = csv.DictReader(io.StringIO(csv_string))
return list(reader)
data = "name,age\nAlice,30\nBob,25"
records = parse_csv(data)
print(records)
# [{'name': 'Alice', 'age': '30'}, {'name': 'Bob', 'age': '25'}]
```
io.StringIO wraps the string as a file-like object that csv.DictReader can consume.
Exercise 3.
Demonstrate the difference between io.StringIO (works with str) and io.BytesIO (works with bytes). Show that writing bytes to StringIO raises an error and vice versa.
Solution to Exercise 3
```python
import io
# StringIO works with str
s = io.StringIO()
s.write("hello")
try:
s.write(b"bytes")
except TypeError as e:
print(f"StringIO error: {e}")
# BytesIO works with bytes
b = io.BytesIO()
b.write(b"hello")
try:
b.write("text")
except TypeError as e:
print(f"BytesIO error: {e}")
```
StringIO accepts only str, and BytesIO accepts only bytes. Mixing them raises TypeError.