Reading Files in Go
last modified May 6, 2026
In this article we show how to read files in Golang. We read text and binary files. Learn how to write to files in Go in Go write file.
To read files in Go, we use the os, io,
bufio and hex packages. (io/ioutil was
deprecated in Go 1.16; use os.ReadFile instead.)
The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece.
We use this text file in some examples.
Go read file into string
The os.ReadFile function reads the whole file into a byte slice.
(Previously ioutil.ReadFile was used; it is now replaced by
os.ReadFile since Go 1.16.) This function is convenient but should
not be used with very large files.
package main
import (
"fmt"
"log"
"os"
)
func main() {
content, err := os.ReadFile("thermopylae.txt")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(content))
}
The example reads the whole file and prints it to the console.
$ go run read_file.go The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece.
Go read file line by line
The Scanner provides a convenient interface for reading data such
as a file of newline-delimited lines of text. It reads data by tokens; the
Split function defines the token. By default, the function breaks
the data into lines with line-termination stripped.
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
f, err := os.Open("thermopylae.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
}
The example reads the file line by line. Each line is printed.
f, err := os.Open("thermopylae.txt")
The Open function opens the file for reading.
defer f.Close()
The file descriptor is closed at the end of the main function.
scanner := bufio.NewScanner(f)
A new scanner is created.
for scanner.Scan() {
fmt.Println(scanner.Text())
}
The Scan advances the Scanner to the next token, which will then be
available through the Bytes or Text function.
Go read file by words
The default split function of a scanner is ScanLines. With
SplitWords, we split the content by words.
package main
import (
"fmt"
"os"
"bufio"
)
func main() {
f, err := os.Open("thermopylae.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
}
}
The example reads the file word by word.
$ go run read_by_word.go The Battle of Thermopylae was fought between ...
Go read file with io.ReadAll
Starting with Go 1.16, the io.ReadAll function reads all data from
an io.Reader until EOF. This is useful when you already have an
open file (or any reader) and want its entire content in a byte slice.
package main
import (
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("thermopylae.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
log.Fatal(err)
}
fmt.Print(string(data))
}
The example opens the file, reads everything with ReadAll, and
prints it without an extra newline.
$ go run read_with_readall.go The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece.
Go read CSV file
The encoding/csv package reads comma‑separated value files.
Each call to Read returns one record (a slice of strings).
1,apple 2,book 3,candle 4,door 5,elephant
We use this simple CSV file.
package main
import (
"encoding/csv"
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("words.csv")
if err != nil {
log.Fatal(err)
}
defer f.Close()
r := csv.NewReader(f)
r.FieldsPerRecord = 2 // optional but safer
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s - %s\n", record[0], record[1])
}
}
The reader returns each line as a string slice. We break when EOF is reached.
r := csv.NewReader(f) r.FieldsPerRecord = 2 // optional but safer
We create a new CSV reader and set the expected number of fields per record.
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s - %s\n", record[0], record[1])
}
In the loop, we read each record and print it. We check for EOF to know when to stop.
$ go run read_csv.go 1 - apple 2 - book 3 - candle 4 - door 5 - elephant
Go read file in chunks
We can read files in chunks of data.
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("thermopylae.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
reader := bufio.NewReader(f)
buf := make([]byte, 16)
for {
n, err := reader.Read(buf)
if n > 0 {
fmt.Print(string(buf[:n]))
}
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
fmt.Println()
}
The example reads the file by small 16 byte portions.
buf := make([]byte, 16)
We define an array of 16 bytes.
for {
n, err := reader.Read(buf)
if n > 0 {
fmt.Print(string(buf[:n]))
}
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
In the for loop, we read data into the buffer with Read, and
print the array buffer to the console with Print.
Go read binary file
The hex package implements hexadecimal encoding and decoding.
package main
import (
"bufio"
"encoding/hex"
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("sid.jpg")
if err != nil {
log.Fatal(err)
}
defer f.Close()
reader := bufio.NewReader(f)
buf := make([]byte, 256)
for {
n, err := reader.Read(buf)
if n > 0 {
fmt.Printf("%s", hex.Dump(buf[:n]))
}
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
}
}
In the code example, we read an image and print it in hexadecimal format.
n, err := reader.Read(buf)
if n > 0 {
fmt.Printf("%s", hex.Dump(buf[:n]))
}
The Read method returns the number of bytes read. We pass only that
slice to hex.Dump so we never output leftover data from a previous
read.
$ go run read_binary_file.go 00000000 ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01 |......JFIF......| 00000010 00 01 00 00 ff e1 00 2f 45 78 69 66 00 00 49 49 |......./Exif..II| 00000020 2a 00 08 00 00 00 01 00 0e 01 02 00 0d 00 00 00 |*...............| 00000030 1a 00 00 00 00 00 00 00 6b 69 6e 6f 70 6f 69 73 |........kinopois| 00000040 6b 2e 72 75 00 ff fe 00 3b 43 52 45 41 54 4f 52 |k.ru....;CREATOR| 00000050 3a 20 67 64 2d 6a 70 65 67 20 76 31 2e 30 20 28 |: gd-jpeg v1.0 (| 00000060 75 73 69 6e 67 20 49 4a 47 20 4a 50 45 47 20 76 |using IJG JPEG v| 00000070 38 30 29 2c 20 71 75 61 6c 69 74 79 20 3d 20 39 |80), quality = 9| 00000080 31 0a ff db 00 43 00 03 02 02 03 02 02 03 03 02 |1....C..........| 00000090 03 03 03 03 03 04 07 05 04 04 04 04 09 06 07 05 |................| 000000a0 07 0a 09 0b 0b 0a 09 0a 0a 0c 0d 11 0e 0c 0c 10 |................| 000000b0 0c 0a 0a 0e 14 0f 10 11 12 13 13 13 0b 0e 14 16 |................| ...
Source
In this article we have covered reading files in Go.
Author
List all Go tutorials.