π Golang File and IO Operations
Your Complete Guide to Working with Files
π The Story: You Are a Librarian
Imagine youβre a librarian in a magical library. Every book is a file. You can:
- Read books to learn whatβs inside
- Write new books or add pages to existing ones
- Control who can access each book (permissions)
- Navigate through shelves and sections (paths)
- Use special tools to read faster (buffered IO)
- Follow standard rules for organizing everything (interfaces)
Letβs explore this library together! ποΈ
π 1. File Reading
What Is It?
Reading a file means opening a book and looking at its pages. Go gives you several ways to peek inside files.
The Simplest Way: Read Everything at Once
package main
import (
"fmt"
"os"
)
func main() {
// Read the whole file at once
data, err := os.ReadFile("story.txt")
if err != nil {
fmt.Println("Oops!", err)
return
}
fmt.Println(string(data))
}
Think of it like: Photocopying an entire book in one go!
Reading Piece by Piece
file, err := os.Open("story.txt")
if err != nil {
fmt.Println("Can't open:", err)
return
}
defer file.Close() // Always close!
buffer := make([]byte, 100)
for {
n, err := file.Read(buffer)
if err != nil {
break
}
fmt.Print(string(buffer[:n]))
}
Think of it like: Reading one chapter at a time!
π Key Points
os.ReadFile()β Quick and easy for small filesos.Open()β When you need more control- Always close files! Use
defer file.Close()
βοΈ 2. File Writing
What Is It?
Writing to a file means adding new pages to a book or creating a brand new book.
The Quickest Way: Write Everything
package main
import "os"
func main() {
message := []byte("Hello, World!")
err := os.WriteFile("hello.txt", message, 0644)
if err != nil {
panic(err)
}
}
Creating and Writing Step by Step
// Create a new file
file, err := os.Create("diary.txt")
if err != nil {
panic(err)
}
defer file.Close()
// Write to it
file.WriteString("Day 1: Learning Go!\n")
file.Write([]byte("Day 2: Files are fun!\n"))
Appending (Adding to the End)
file, err := os.OpenFile("diary.txt",
os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
panic(err)
}
defer file.Close()
file.WriteString("Day 3: I'm getting better!\n")
Think of it like: Adding new pages to your diary without erasing old ones!
π 3. File Modes and Permissions
What Are They?
Permissions are like library rules: who can read, write, or enter certain sections.
Understanding Permission Numbers
Permission: 0644
β
Owner: 6 (read + write) = rw-
Group: 4 (read only) = r--
Others: 4 (read only) = r--
Common Permission Codes
| Code | Owner | Group | Others | Use Case |
|---|---|---|---|---|
| 0644 | rw- | rβ | rβ | Regular files |
| 0755 | rwx | r-x | r-x | Executable files |
| 0600 | rw- | β | β | Private files |
File Open Flags
// Flags you can combine with |
os.O_RDONLY // Read only
os.O_WRONLY // Write only
os.O_RDWR // Read and write
os.O_CREATE // Create if missing
os.O_APPEND // Add to end
os.O_TRUNC // Erase first
Example: Specific Permissions
file, err := os.OpenFile("secret.txt",
os.O_CREATE|os.O_WRONLY,
0600) // Only owner can read/write
πΊοΈ 4. Path Manipulation
What Is It?
Paths are directions to find books in our library. The filepath package helps you navigate!
Building Paths Safely
import "path/filepath"
// Join paths the right way
path := filepath.Join("home", "user", "docs")
// Result: "home/user/docs" (or "home\user\docs" on Windows)
Why use Join? It automatically uses the right slash for your operating system!
Useful Path Operations
fullPath := "/home/user/docs/report.pdf"
// Get the directory
dir := filepath.Dir(fullPath)
// Result: "/home/user/docs"
// Get the filename
name := filepath.Base(fullPath)
// Result: "report.pdf"
// Get the extension
ext := filepath.Ext(fullPath)
// Result: ".pdf"
// Get absolute path
abs, _ := filepath.Abs("./file.txt")
// Result: Full path from root
Walking Through Directories
filepath.Walk("/home/user", func(
path string,
info os.FileInfo,
err error) error {
if err != nil {
return err
}
fmt.Println(path)
return nil
})
Think of it like: Walking through every aisle in the library!
π 5. IO Interfaces
What Are They?
Interfaces are standard plugs that let different things work together. The two most important are:
The Reader Interface
type Reader interface {
Read(p []byte) (n int, err error)
}
Anything that can be read from implements this:
- Files
- Network connections
- Strings
- HTTP responses
The Writer Interface
type Writer interface {
Write(p []byte) (n int, err error)
}
Anything you can write to implements this.
Why Interfaces Are Magic
import "io"
// This function works with ANY reader!
func countBytes(r io.Reader) (int, error) {
buf := make([]byte, 1024)
total := 0
for {
n, err := r.Read(buf)
total += n
if err != nil {
break
}
}
return total, nil
}
Copying Data Easily
// Copy from any Reader to any Writer
io.Copy(destinationWriter, sourceReader)
// Example: Copy file
src, _ := os.Open("source.txt")
dst, _ := os.Create("copy.txt")
io.Copy(dst, src)
π 6. Buffered IO
What Is It?
Buffered IO is like having a shopping cart. Instead of making one trip per item, you collect items and make fewer trips!
Why Buffering Matters
Without buffering: 100 small writes = 100 disk operations π° With buffering: 100 small writes = 1 disk operation! π
Buffered Reading
import "bufio"
file, _ := os.Open("bigfile.txt")
reader := bufio.NewReader(file)
// Read line by line efficiently
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
fmt.Print(line)
}
Buffered Writing
file, _ := os.Create("output.txt")
writer := bufio.NewWriter(file)
writer.WriteString("Line 1\n")
writer.WriteString("Line 2\n")
writer.WriteString("Line 3\n")
// DON'T FORGET: Flush sends data to disk!
writer.Flush()
file.Close()
Scanner: The Easiest Way to Read Lines
file, _ := os.Open("list.txt")
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fmt.Println("Found:", line)
}
Pro Tip: Scanner handles line endings automatically! π―
π 7. The io/fs Package
What Is It?
The io/fs package provides a universal way to work with any file system β real files, embedded files, zip archives, or even remote storage!
The FS Interface
type FS interface {
Open(name string) (File, error)
}
Working with the OS File System
import "io/fs"
// Walk through files
fs.WalkDir(os.DirFS("."), ".", func(
path string,
d fs.DirEntry,
err error) error {
fmt.Println(path)
return nil
})
Reading Files from Any FS
// Create a filesystem rooted at a directory
myFS := os.DirFS("/home/user/data")
// Read a file from it
data, err := fs.ReadFile(myFS, "config.json")
Checking File Info
info, err := fs.Stat(myFS, "file.txt")
if err != nil {
panic(err)
}
fmt.Println("Name:", info.Name())
fmt.Println("Size:", info.Size())
fmt.Println("Is Dir:", info.IsDir())
fmt.Println("Mode:", info.Mode())
Embedded Files (Super Cool!)
import "embed"
//go:embed templates/*
var templates embed.FS
// Now use templates as a filesystem!
data, _ := fs.ReadFile(templates, "templates/index.html")
Think of it like: One remote control that works with every TV brand!
π― Quick Summary
graph LR A["π File Operations"] --> B["π Reading"] A --> C["βοΈ Writing"] A --> D["π Permissions"] A --> E["πΊοΈ Paths"] A --> F["π Interfaces"] A --> G["π Buffered IO"] A --> H["π io/fs"] B --> B1["os.ReadFile"] B --> B2["os.Open + Read"] C --> C1["os.WriteFile"] C --> C2["os.Create"] C --> C3["Append mode"] F --> F1["io.Reader"] F --> F2["io.Writer"] G --> G1["bufio.Reader"] G --> G2["bufio.Writer"] G --> G3["bufio.Scanner"]
π You Did It!
You now understand how Go handles files like a pro librarian:
- Read files quickly or piece by piece
- Write new content or append to existing files
- Control access with permissions and modes
- Navigate paths safely across any OS
- Use interfaces for flexible, reusable code
- Buffer your IO for speed
- Abstract filesystems with io/fs
Next step: Try creating a small program that reads a file, modifies it, and writes it back! Youβve got this! πͺ
