Go File Handling
File handling covers reading data from files and writing data to files. Go provides the os package for low-level file operations and the bufio package for efficient buffered reading. The os package also provides a higher-level helper through os.ReadFile and os.WriteFile for simple cases.
File Operations Overview
File Operations in Go
├── Create a new file os.Create()
├── Open an existing file os.Open()
├── Read entire file os.ReadFile()
├── Write entire file os.WriteFile()
├── Read line by line bufio.Scanner
├── Write with buffer bufio.Writer
└── Close a file file.Close() ← always defer this
Writing to a File
Simple Write – os.WriteFile
package main
import (
"fmt"
"os"
)
func main() {
content := []byte("Hello, File!\nWelcome to Go file handling.\n")
err := os.WriteFile("notes.txt", content, 0644)
if err != nil {
fmt.Println("Error writing file:", err)
return
}
fmt.Println("File written successfully")
}
The 0644 is a Unix permission code: owner can read and write; everyone else can read.
Reading from a File
Simple Read – os.ReadFile
package main
import (
"fmt"
"os"
)
func main() {
data, err := os.ReadFile("notes.txt")
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Println(string(data))
}
Output:
Hello, File!
Welcome to Go file handling.
Reading a File Line by Line
For large files, reading line by line with bufio.Scanner is more memory-efficient than loading the whole file at once.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("notes.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close() // always close the file
scanner := bufio.NewScanner(file)
lineNumber := 1
for scanner.Scan() {
fmt.Printf("Line %d: %s\n", lineNumber, scanner.Text())
lineNumber++
}
if err := scanner.Err(); err != nil {
fmt.Println("Scanner error:", err)
}
}
Output:
Line 1: Hello, File!
Line 2: Welcome to Go file handling.
File Read/Write Flow
WRITING:
Program → os.WriteFile() → creates/overwrites file on disk
READING (whole file):
File on disk → os.ReadFile() → []byte in memory → string(data)
READING (line by line):
File on disk → os.Open() → bufio.Scanner → one line at a time
Appending to a File
Open the file with the append and write-only flags to add content without overwriting.
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.OpenFile("notes.txt", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("Error:", err)
return
}
defer file.Close()
_, err = file.WriteString("This line was appended.\n")
if err != nil {
fmt.Println("Error writing:", err)
return
}
fmt.Println("Content appended")
}
Common File Open Flags
| Flag | Meaning |
|---|---|
os.O_RDONLY | Open for reading only |
os.O_WRONLY | Open for writing only |
os.O_RDWR | Open for reading and writing |
os.O_CREATE | Create file if it does not exist |
os.O_APPEND | Append to end of file |
os.O_TRUNC | Truncate file to zero length on open |
Checking If a File Exists
package main
import (
"errors"
"fmt"
"os"
)
func fileExists(name string) bool {
_, err := os.Stat(name)
return !errors.Is(err, os.ErrNotExist)
}
func main() {
fmt.Println(fileExists("notes.txt")) // true or false
}
Deleting a File
err := os.Remove("notes.txt")
if err != nil {
fmt.Println("Could not delete file:", err)
}
Key Points
- Use
os.WriteFileandos.ReadFilefor simple whole-file operations - Always call
defer file.Close()immediately after opening a file - Use
bufio.Scannerto read large files line by line without loading all into memory - Use
os.OpenFilewith flags likeos.O_APPENDfor more control over how the file opens - Check for
os.ErrNotExistto determine if a file is missing before operating on it
