File I/O in Go
Go provides comprehensive support for file operations through the os, io, bufio, and ioutil packages. Understanding these packages is essential for working with files in Go applications.
Reading Files
Reading Entire Files
The simplest way to read a file:
package main
import (
"fmt"
"os"
)
func main() {
// Read entire file at once
data, err := os.ReadFile("example.txt")
if err != nil {
fmt.Println("Error reading file:", err)
return
}
fmt.Println("File contents:")
fmt.Println(string(data))
}For Go versions before 1.16, use ioutil.ReadFile:
import "io/ioutil"
// data, err := ioutil.ReadFile("example.txt")
Reading Files Line by Line
For large files, read line by line:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
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("Error reading file:", err)
}
}Reading with Buffer
Using a buffered reader for more control:
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
reader := bufio.NewReader(file)
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
// Process last line if it doesn't end with newline
if line != "" {
fmt.Print("Last line:", line)
}
break
}
fmt.Println("Error reading:", err)
break
}
fmt.Print("Line:", line)
}
}Writing Files
Writing Entire Files
package main
import (
"fmt"
"os"
)
func main() {
data := "Hello, World!\nThis is a test file."
// Write to file (creates or truncates)
err := os.WriteFile("output.txt", []byte(data), 0644)
if err != nil {
fmt.Println("Error writing file:", err)
return
}
fmt.Println("File written successfully")
}Writing with File Handle
For more control over writing:
package main
import (
"fmt"
"os"
)
func main() {
// Create or truncate file
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
// Write data
data := []byte("Hello, World!\n")
_, err = file.Write(data)
if err != nil {
fmt.Println("Error writing to file:", err)
return
}
// Write string
_, err = file.WriteString("This is another line.\n")
if err != nil {
fmt.Println("Error writing string:", err)
return
}
fmt.Println("Data written successfully")
}Appending to Files
package main
import (
"fmt"
"os"
)
func main() {
// Open file for appending (creates if doesn't exist)
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()
// Append data
logEntry := "New log entry at " + time.Now().Format(time.RFC3339) + "\n"
_, err = file.WriteString(logEntry)
if err != nil {
fmt.Println("Error appending to file:", err)
return
}
fmt.Println("Log entry appended")
}Buffered Writing
Using bufio.Writer for efficient writing:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Create("buffered_output.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
// Create buffered writer
writer := bufio.NewWriter(file)
// Write multiple lines
for i := 1; i <= 10; i++ {
_, err := fmt.Fprintf(writer, "Line number %d\n", i)
if err != nil {
fmt.Println("Error writing line:", err)
return
}
}
// Flush buffer to ensure all data is written
err = writer.Flush()
if err != nil {
fmt.Println("Error flushing buffer:", err)
return
}
fmt.Println("Buffered writing completed")
}Working with Directories
Creating Directories
package main
import (
"fmt"
"os"
)
func main() {
// Create single directory
err := os.Mkdir("newdir", 0755)
if err != nil {
fmt.Println("Error creating directory:", err)
return
}
// Create nested directories
err = os.MkdirAll("parent/child/grandchild", 0755)
if err != nil {
fmt.Println("Error creating directories:", err)
return
}
fmt.Println("Directories created successfully")
}Reading Directories
package main
import (
"fmt"
"os"
)
func main() {
// Read directory contents
entries, err := os.ReadDir(".")
if err != nil {
fmt.Println("Error reading directory:", err)
return
}
fmt.Println("Directory contents:")
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
continue
}
fileType := "file"
if entry.IsDir() {
fileType = "directory"
}
fmt.Printf("%s: %s (%d bytes)\n", fileType, entry.Name(), info.Size())
}
}Walking Directory Trees
package main
import (
"fmt"
"os"
"path/filepath"
)
func visit(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
indent := strings.Repeat(" ", strings.Count(path, string(os.PathSeparator))-1)
fmt.Printf("%s%s\n", indent, info.Name())
return nil
}
func main() {
root := "."
err := filepath.Walk(root, visit)
if err != nil {
fmt.Println("Error walking directory:", err)
}
}File Information
Getting file metadata:
package main
import (
"fmt"
"os"
)
func main() {
// Get file info
info, err := os.Stat("example.txt")
if err != nil {
fmt.Println("Error getting file info:", err)
return
}
fmt.Printf("Name: %s\n", info.Name())
fmt.Printf("Size: %d bytes\n", info.Size())
fmt.Printf("Mode: %s\n", info.Mode())
fmt.Printf("Modified: %s\n", info.ModTime())
fmt.Printf("Is directory: %t\n", info.IsDir())
}Copying Files
package main
import (
"fmt"
"io"
"os"
)
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer sourceFile.Close()
destFile, err := os.Create(dst)
if err != nil {
return err
}
defer destFile.Close()
_, err = io.Copy(destFile, sourceFile)
if err != nil {
return err
}
// Copy permissions
sourceInfo, err := os.Stat(src)
if err != nil {
return err
}
return os.Chmod(dst, sourceInfo.Mode())
}
func main() {
err := copyFile("source.txt", "destination.txt")
if err != nil {
fmt.Println("Error copying file:", err)
return
}
fmt.Println("File copied successfully")
}Temporary Files
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
// Create temporary file
tempFile, err := ioutil.TempFile("", "example_*.txt")
if err != nil {
fmt.Println("Error creating temp file:", err)
return
}
defer os.Remove(tempFile.Name()) // Clean up
defer tempFile.Close()
// Write to temp file
_, err = tempFile.WriteString("This is temporary data")
if err != nil {
fmt.Println("Error writing to temp file:", err)
return
}
fmt.Printf("Temp file created: %s\n", tempFile.Name())
// Create temporary directory
tempDir, err := ioutil.TempDir("", "example_dir")
if err != nil {
fmt.Println("Error creating temp dir:", err)
return
}
defer os.RemoveAll(tempDir) // Clean up
fmt.Printf("Temp directory created: %s\n", tempDir)
}Error Handling
Proper error handling for file operations:
package main
import (
"fmt"
"os"
)
func safeFileOperation(filename string) error {
// Check if file exists
if _, err := os.Stat(filename); os.IsNotExist(err) {
return fmt.Errorf("file %s does not exist", filename)
}
// Attempt to open file
file, err := os.Open(filename)
if err != nil {
return fmt.Errorf("failed to open file %s: %w", filename, err)
}
defer file.Close()
// Perform operations...
return nil
}
func main() {
err := safeFileOperation("nonexistent.txt")
if err != nil {
fmt.Println("Operation failed:", err)
}
}Best Practices
- Always close files: Use
defer file.Close()immediately after opening - Handle errors: Check for errors on every file operation
- Use appropriate permissions: 0644 for files, 0755 for directories
- Buffer I/O operations: Use
bufiofor better performance - Check file existence: Before operations that require files to exist
- Clean up temporary files: Remove temp files when done
- Use relative paths carefully: Be aware of working directory
- Handle large files: Don’t read entire large files into memory
- Use os.OpenFile: For fine-grained control over file opening
- Consider atomic operations: For critical file updates
Go’s file I/O is straightforward but powerful. The standard library provides everything you need for most file operations, with good performance and error handling.
For more on JSON handling, check our JSON tutorial. If you need to work with time, see the time handling tutorial.