Reading or writing one byte at a time is expensive.
Each filesystem operation may require a system call. System calls are much slower than ordinary memory access. Programs that read or write small pieces of data usually collect bytes in memory first.
This technique is called buffering.
A buffered reader fetches a large block of bytes from the operating system, then serves smaller reads from memory.
A buffered writer collects bytes in memory and writes them out in larger blocks.
The standard library provides both.
const std = @import("std");
pub fn main() !void {
const cwd = std.fs.cwd();
const file = try cwd.openFile("message.txt", .{});
defer file.close();
var buffered = std.io.bufferedReader(file.reader());
const reader = buffered.reader();
var buffer: [128]u8 = undefined;
while (true) {
const n = try reader.read(&buffer);
if (n == 0)
break;
try std.io.getStdOut().writeAll(
buffer[0..n],
);
}
}The expression:
file.reader()creates a reader interface over the file.
The next line wraps it:
std.io.bufferedReader(file.reader())The buffered reader keeps an internal memory buffer. Small reads come from this buffer instead of directly from the operating system.
The returned object is:
bufferedTo access the actual reader interface:
const reader = buffered.reader();The rest of the code looks the same as earlier examples.
Buffered writing is similar.
const std = @import("std");
pub fn main() !void {
const cwd = std.fs.cwd();
const file = try cwd.createFile(
"numbers.txt",
.{},
);
defer file.close();
var buffered = std.io.bufferedWriter(
file.writer(),
);
const writer = buffered.writer();
var i: usize = 1;
while (i <= 10) : (i += 1) {
try writer.print("{d}\n", .{i});
}
try buffered.flush();
}The call:
std.io.bufferedWriter(file.writer())creates a buffered writer.
The loop writes formatted text into the buffer, not directly to the file.
At the end:
try buffered.flush();forces any remaining buffered bytes to be written.
This step is important.
A buffered writer may still contain unwritten data when the program ends. flush ensures the bytes actually reach the file.
Many buffered systems flush automatically when closed, but explicit flushing makes the program’s behavior clear.
Buffering is most useful when:
- reading small pieces repeatedly
- writing many short strings
- processing text line by line
- working with slow devices or network streams
Large block operations may already be efficient without extra buffering.
The standard library uses readers and writers heavily. Files, standard input, standard output, sockets, compressed streams, and in-memory streams often expose the same interfaces.
This allows code like:
try writer.writeAll(bytes);to work with many different output destinations.
Exercise 13-13. Remove the buffered reader and compare performance on a large file.
Exercise 13-14. Modify the buffered writer example so it writes 100000 numbers.
Exercise 13-15. Write a program that copies standard input to standard output using buffered streams.
Exercise 13-16. Write a program that reads a file one line at a time using a buffered reader.