Skip to main content
Reading files is a fundamental task in Go programming. This guide covers different approaches to reading files, from simple one-shot reads to more controlled operations.

Basic File Reading

The simplest way to read an entire file into memory is using os.ReadFile:
package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func main() {
	// Read entire file contents into memory
	path := filepath.Join(os.TempDir(), "dat")
	dat, err := os.ReadFile(path)
	check(err)
	fmt.Print(string(dat))
}
Always check errors when performing file operations. The helper function check streamlines error handling throughout these examples.

Controlled Reading with os.Open

For more control over file reading operations, open a file to obtain an os.File value:
// Open file for reading
f, err := os.Open(path)
check(err)
defer f.Close()

// Read specific number of bytes
b1 := make([]byte, 5)
n1, err := f.Read(b1)
check(err)
fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))
Always close files when done. Use defer f.Close() immediately after opening to ensure proper cleanup.

Seeking in Files

The Seek method allows you to navigate to specific positions in a file:

Seek from Start

// Seek to position 6 from start
o2, err := f.Seek(6, io.SeekStart)
check(err)
b2 := make([]byte, 2)
n2, err := f.Read(b2)
check(err)
fmt.Printf("%d bytes @ %d: %v\n", n2, o2, string(b2[:n2]))

Seek from Current Position

// Seek 2 bytes forward from current position
_, err = f.Seek(2, io.SeekCurrent)
check(err)

Seek from End

// Seek to 4 bytes before end of file
_, err = f.Seek(-4, io.SeekEnd)
check(err)

Rewind to Beginning

// Reset to beginning of file
_, err = f.Seek(0, io.SeekStart)
check(err)

Advanced Reading with io Package

The io package provides robust functions for file reading:
import "io"

// ReadAtLeast ensures minimum bytes are read
o3, err := f.Seek(6, io.SeekStart)
check(err)
b3 := make([]byte, 2)
n3, err := io.ReadAtLeast(f, b3, 2)
check(err)
fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))
io.ReadAtLeast is more robust than Read for ensuring a minimum number of bytes are read.

Buffered Reading

The bufio package provides buffered reading for improved efficiency:
import "bufio"

// Create buffered reader
r4 := bufio.NewReader(f)

// Peek at bytes without consuming them
b4, err := r4.Peek(5)
check(err)
fmt.Printf("5 bytes: %s\n", string(b4))

Common Patterns

Full File Read

Use os.ReadFile when you need the entire file contents at once

Partial Reading

Use os.Open with Read for reading specific portions

Seeking

Use Seek to navigate to specific file positions

Buffered I/O

Use bufio.NewReader for efficient small reads and additional methods

Best Practices

File operations can fail for many reasons. Always check and handle errors appropriately.
Use defer f.Close() immediately after opening to ensure files are closed even if errors occur.
  • Use os.ReadFile for small files that fit in memory
  • Use os.Open with Read for large files or when you need partial reads
  • Use bufio.NewReader when making many small reads
Pre-allocate byte slices with appropriate sizes to avoid unnecessary allocations during reading.

Writing Files

Learn how to write data to files

File Paths

Working with file paths across operating systems

Build docs developers (and LLMs) love