bufio wraps a Reader or Writer to cut down on syscalls, and adds line/word/rune scanning.
The smallest useful thing bufio does is read one line a person typed. Reading os.Stdin a byte at a time costs a system call per byte, so you wrap it once and read whole lines out of the buffer.
NewReaderwraps standard input in a 4096-byte buffer.ReadStringreads until it meets the byte you hand it.textkeeps the newline you typed, sostrings.TrimSpaceis the usual next call.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// Create a buffered reader from standard input
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
// Read until the newline character
text, err := reader.ReadString('\n')
if err != nil {
fmt.Println("Error reading:", err)
return
}
fmt.Println("You entered:", text)
}$ go run main.go
Enter text: Hello, gopher
You entered: Hello, gopher
Examples
Reads one line at a time instead of loading the whole file.
file, err := os.Open("access.log")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}2026-08-06 09:14:02 GET /health 200
2026-08-06 09:14:03 GET /metrics 200Split swaps the tokenizer.
scanner := bufio.NewScanner(strings.NewReader("the quick brown fox"))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}the
quick
brown
foxInspect (peek)
r := bufio.NewReader(strings.NewReader("Gopher"))
b, _ := r.Peek(2)
fmt.Println(string(b))
s, _ := r.ReadString('\n')
fmt.Println(s)Go
GopherFlush is mandatory, unwritten bytes never reach disk.
f, err := os.Create("report.csv")
if err != nil {
log.Fatal(err)
}
defer f.Close()
rows := []string{"id,total", "1,42.50", "2,17.00"}
w := bufio.NewWriter(f)
for _, row := range rows {
fmt.Fprintln(w, row)
}
if err := w.Flush(); err != nil {
log.Fatal(err)
}
fmt.Println("wrote", len(rows), "rows")wrote 3 rows| Function | Description |
|---|---|
NewReader(rd io.Reader) *Reader r := bufio.NewReader(f) | Wraps rd in a Reader with a 4096-byte default buffer. |
NewReaderSize(rd io.Reader, size int) *Reader r := bufio.NewReaderSize(f, 8192) | Wraps rd in a Reader with a buffer of at least size bytes. |
NewWriter(w io.Writer) *Writer bw := bufio.NewWriter(f) | Wraps w in a Writer with a 4096-byte default buffer. |
NewWriterSize(w io.Writer, size int) *Writer bw := bufio.NewWriterSize(f, 8192) | Wraps w in a Writer with a buffer of at least size bytes. |
NewReadWriter(r *Reader, w *Writer) *ReadWriter rw := bufio.NewReadWriter(r, w) | Combines a Reader and a Writer into one value. |
NewScanner(r io.Reader) *Scanner s := bufio.NewScanner(f) | Wraps r in a Scanner; reading starts on the first Scan call. |
(*Reader) Read(p []byte) (n int, err error) | Reads up to len(p) bytes into p; implements io.Reader. |
(*Reader) ReadByte() (byte, error) | Reads and returns a single byte. |
(*Reader) UnreadByte() error | Un-reads the last byte returned by ReadByte. |
(*Reader) ReadRune() (r rune, size int, err error) | Reads and returns a single UTF-8-encoded rune and its byte width. |
(*Reader) UnreadRune() error | Un-reads the last rune returned by ReadRune. |
(*Reader) ReadLine() (line []byte, isPrefix bool, err error) | Reads one line with the newline stripped; isPrefix reports whether the line didn't fit in the buffer. Deprecated in favor of ReadString or a Scanner. |
(*Reader) ReadSlice(delim byte) (line []byte, err error) line, err := r.ReadSlice('\n') | Reads until delim, returning a slice that points into the internal buffer. |
Three methods read up to a delimiter, differing only in what they hand back. | |
(*Reader) ReadBytes(delim byte) ([]byte, error) b, err := r.ReadBytes('\n') | Reads until delim, returning a copy of the bytes read, delim included. |
(*Reader) ReadString(delim byte) (string, error) line, err := r.ReadString('\n') | Reads until delim, returning a string, delim included. |
(*Reader) Peek(n int) ([]byte, error) b, err := r.Peek(4) | Returns the next n bytes without advancing the reader; the slice points into the internal buffer. |
(*Reader) Discard(n int) (discarded int, err error) | Skips the next n bytes without returning them. |
(*Reader) Buffered() int | Returns the number of bytes currently available in the buffer. |
(*Reader) Size() int | Returns the size of the underlying buffer in bytes. |
(*Reader) Reset(r io.Reader) | Discards buffered data and switches the Reader to read from r. |
(*Reader) WriteTo(w io.Writer) (n int64, err error) | Writes all buffered and unread data to w; implements io.WriterTo. |
(*Writer) Write(p []byte) (n int, err error) | Buffers p, flushing automatically if it doesn't fit; implements io.Writer. |
(*Writer) WriteByte(c byte) error | Buffers a single byte. |
(*Writer) WriteRune(r rune) (size int, err error) | Buffers the UTF-8 encoding of rune r. |
(*Writer) WriteString(s string) (int, error) | Buffers the contents of s. |
(*Writer) Flush() error err := bw.Flush() | Writes all buffered data to the underlying writer. |
(*Writer) Available() int | Returns how many bytes are still unused in the buffer. |
(*Writer) AvailableBuffer() []byte | Returns an empty slice with Available() capacity, for appending to before a Write call. |
(*Writer) Buffered() int | Returns the number of bytes already buffered. |
(*Writer) Size() int | Returns the size of the underlying buffer in bytes. |
(*Writer) Reset(w io.Writer) | Discards buffered data and switches the Writer to write to w. |
(*Writer) ReadFrom(r io.Reader) (n int64, err error) | Reads from r until EOF, buffering as it goes; implements io.ReaderFrom. |
type ReadWriter struct{ *Reader; *Writer } | Embeds a *Reader and a *Writer in one value, so it satisfies both io.Reader and io.Writer. |
(*Scanner) Scan() bool for s.Scan() { ... } | Advances to the next token; returns false at EOF or on error. |
(*Scanner) Bytes() []byte | Returns the most recent token as a byte slice, valid only until the next Scan call. |
(*Scanner) Text() string line := s.Text() | Returns the most recent token as a string, valid only until the next Scan call. |
(*Scanner) Err() error if err := s.Err(); err != nil { ... } | Returns the first non-EOF error encountered by Scan. |
(*Scanner) Split(split SplitFunc) s.Split(bufio.ScanWords) | Sets the tokenizer; must be called before the first Scan. |
(*Scanner) Buffer(buf []byte, max int) s.Buffer(buf, 1<<20) | Sets the initial buffer and the maximum token size Scan will allocate. |
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error) | Signature every Scanner tokenizer implements: consume advance bytes of data, optionally emit token. |
A | |
ScanLines(data, atEOF) (advance int, token []byte, err error) s.Split(bufio.ScanLines) | Splits on newlines, stripping the line ending. The Scanner's default. |
ScanWords(data, atEOF) (advance int, token []byte, err error) s.Split(bufio.ScanWords) | Splits on runs of whitespace, discarding empty fields. |
ScanRunes(data, atEOF) (advance int, token []byte, err error) s.Split(bufio.ScanRunes) | Splits into individual UTF-8-encoded runes. |
ScanBytes(data, atEOF) (advance int, token []byte, err error) s.Split(bufio.ScanBytes) | Splits into individual bytes. |
MaxScanTokenSize | Default maximum size of a single token: 64 * 1024 bytes. |
ErrTooLong | Returned by Scan when a token exceeds the buffer's maximum size. |
ErrFinalToken | Sentinel a SplitFunc returns as its error to stop Scan after this token without signaling failure. |
ErrBufferFull | Returned by ReadSlice when a token doesn't fit in the buffer. |
ErrNegativeCount | Returned by Peek when called with a negative count. |
Related
Gotchas
Peek,ReadSlice,Scanner.Bytespoint into the internal buffer, copy them to keep them.- Default
Scannercaps tokens at 64KB. Usescanner.Bufferfor longer lines. - Not safe for concurrent use.