Working with the File System in Go: A Beginner's Guide
Go (Golang) is a modern compiled language known for its simplicity, performance, and built-in tools. One of the key tasks for any backend developer is interacting with the file system: reading configurations, writing logs, creating temporary files and directories. In this article, we will cover the basic operations with files and folders in Go, starting from the very basics. The material is designed for beginners, so each step will be accompanied by clear code examples.
Main Packages for Working with Files
The Go standard library has two main packages for working with the file system:
os— low-level work with files, folders, permissions, and processes.io/ioutil(deprecated in Go 1.16, but still encountered) — convenient functions for reading/writing entire files.io— interfaces for streaming input/output.bufio— buffered reading/writing (useful for large files).
Starting from Go 1.16, it is recommended to use functions from the os package (os.ReadFile, os.WriteFile) instead of ioutil. We will rely on the modern approach.
Reading Files
The simplest way to read a small file entirely is to use os.ReadFile. It returns a byte slice and an error.
package main
import ( "fmt" "os")
func main() { // Reading the entire file data, err := os.ReadFile("example.txt") if err != nil { fmt.Println("Error reading file:", err) return } fmt.Println("File contents:") fmt.Println(string(data))}If the file is large (e.g., a log file hundreds of megabytes in size), it is better to read it line by line or in blocks using bufio.Scanner:
package main
import ( "bufio" "fmt" "os")
func main() { file, err := os.Open("bigfile.log") if err != nil { fmt.Println("Error opening file:", err) return } defer file.Close() // always close the file
scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() fmt.Println(line) // process the line }
if err := scanner.Err(); err != nil { fmt.Println("Scanning error:", err) }}Note the defer file.Close() — this ensures the file will be closed after the function completes, even if an error occurs.
Writing to Files
To write data to a file, use os.WriteFile (creates/overwrites the file) or os.Create + Write (for more flexible writing).
package main
import ( "fmt" "os")
func main() { // Simple string write to a file text := "Hello, world!Second line." err := os.WriteFile("output.txt", []byte(text), 0644) if err != nil { fmt.Println("Write error:", err) return } fmt.Println("File successfully written")}The parameter 0644 is the Unix-style permission (owner can read/write, others can only read).
If you need to append data to the end of a file, use os.OpenFile with the os.O_APPEND flag:
package main
import ( "fmt" "os")
func main() { file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Println("Error opening file:", err) return } defer file.Close()
_, err = file.WriteString("New log entry") if err != nil { fmt.Println("Write error:", err) }}