Skip to main content

File Handling

Status

This note is complete, reviewed, and considered stable.

File operations are a core part of many Go applications. Go provides a rich set of APIs in the os, io, and io/fs packages for file and directory management. This note covers file creation, reading, writing, permissions, metadata, directory operations, and best practices.

File Creation and Opening​

Go offers several ways to create and open files, each suited for different scenarios:

  • Create a new file (or truncate if exists):

    file, err := os.Create("example.txt") // O_WRONLY|O_CREATE|O_TRUNC
    if err != nil { log.Fatal(err) }
    defer file.Close()
  • Open an existing file for reading:

    file, err := os.Open("example.txt") // O_RDONLY
    if err != nil { log.Fatal(err) }
    defer file.Close()
  • Open a file with custom flags and permissions:

    file, err := os.OpenFile("example.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil { log.Fatal(err) }
    defer file.Close()

File Flags​

  • os.O_RDONLY: read-only
  • os.O_WRONLY: write-only
  • os.O_RDWR: read-write
  • os.O_CREATE: create if not exists
  • os.O_APPEND: append to file
  • os.O_TRUNC: truncate file when opening

File Permissions​

Permissions are specified as an octal number (e.g., 0644). They control who can read, write, or execute the file:

  • 0400: read-only (owner)
  • 0600: read/write (owner)
  • 0644: owner read/write, others read
  • 0755: owner all, others read/execute

Writing to Files​

We can write to files using several methods:

  • Write bytes:

    _, err := file.Write([]byte("Hello, World!\n"))
  • Write strings:

    _, err := file.WriteString("Another line\n")
  • Formatted output:

    fmt.Fprintf(file, "Number: %d\n", 42)

For simple cases, use os.WriteFile (Go 1.16+):

err := os.WriteFile("output.txt", []byte("Quick write!"), 0644)

Reading from Files​

There are multiple ways to read files in Go:

  • Read bytes:

    data := make([]byte, 100)
    n, err := file.Read(data)
    fmt.Println(string(data[:n]))
  • Buffered reading (for large files):

    reader := bufio.NewReader(file)
    line, err := reader.ReadString('\n')
    fmt.Println(line)
  • Read entire file (small files):

    content, err := os.ReadFile("example.txt")
    if err != nil { log.Fatal(err) }
    fmt.Println(string(content))

Closing Files​

Always close files after use to free resources. The idiomatic way is:

defer file.Close()

File Metadata and Existence​

Use os.Stat to get file info and check existence:

info, err := os.Stat("example.txt")
if os.IsNotExist(err) {
fmt.Println("File does not exist")
} else {
fmt.Println("Size:", info.Size())
fmt.Println("Permissions:", info.Mode())
fmt.Println("IsDir:", info.IsDir())
fmt.Println("ModTime:", info.ModTime())
}

Deleting, Renaming, and Moving Files​

  • Delete a file:

    err := os.Remove("example.txt")
  • Rename or move a file:

    err := os.Rename("old.txt", "new.txt")

Working with Directories​

  • Create a single directory:

    err := os.Mkdir("mydir", 0755)
    • Fails if parent does not exist.
  • Create nested directories:

    err := os.MkdirAll("parent/child/grandchild", 0755)
    • Creates all missing parents in the path.

Difference: os.Mkdir creates only one directory and fails if the parent is missing. os.MkdirAll creates all necessary parent directories.

  • List files in a directory:

    files, err := os.ReadDir(".")
    for _, f := range files {
    fmt.Println(f.Name())
    }

File and Directory Permissions​

Permissions are crucial for security. Always set the minimum required permissions. Use os.Chmod to change permissions:

err := os.Chmod("example.txt", 0600)