Skip to main content
eLearner.app
Module 8 · Lesson 3 of 538/50 in the course~14 min
Module lessons (3/5)

`io` and `os`: files and streams

Reading and writing files and streams in Go revolves around two complementary packages: os (opening, creation, metadata, processes) and io (the Reader/Writer interfaces that abstract any byte source).

Reading and writing files: the shortcuts

For small or configuration files, a single call is enough:

Go
data, err := os.ReadFile("input.txt")        // returns []byte
if err != nil {
    return err
}
fmt.Println(string(data))

err = os.WriteFile("out.txt", []byte("ciao"), 0644)

os.ReadFile opens, reads everything into memory and closes. Perfect up to a few MB; beyond that, streaming is better.

Opening files for streaming

For large files or incremental access, open the file and close it with defer:

Go
f, err := os.Open("big.txt")        // read-only
if err != nil {
    return err
}
defer f.Close()

buf := make([]byte, 4096)
for {
    n, err := f.Read(buf)
    if n > 0 {
        process(buf[:n])
    }
    if err == io.EOF {
        break
    }
    if err != nil {
        return err
    }
}

os.Open opens in read-only. To write use os.Create (truncates if it exists) or os.OpenFile(path, flag, perm) with flags like os.O_APPEND | os.O_CREATE | os.O_WRONLY.

io.Reader and io.Writer: the universal interfaces

Go
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }

Wherever the standard library accepts an io.Reader you can pass a file (*os.File), a string (strings.NewReader), a *bytes.Buffer, an HTTP response (resp.Body), a decompressor... The same goes for io.Writer.

Go
// Copy from a Reader to a Writer (e.g. download → file).
n, err := io.Copy(dst, src)

io.Copy is the workhorse for byte pipelines: it handles buffering and EOF correctly. io.ReadAll(r) reads everything until EOF into a []byte.

os.Args: CLI arguments

os.Args is a slice of strings: index 0 is the program name, from 1 on the actual arguments:

Go
func main() {
    if len(os.Args) < 2 {
        fmt.Fprintln(os.Stderr, "uso: prog <nome>")
        os.Exit(1)
    }
    fmt.Println("ciao", os.Args[1])
}

For more structured parsing (flags, defaults, help) use the flag package from the stdlib or external libraries like cobra.

os.Stdin, os.Stdout, os.Stderr

These are three global *os.Files: wherever an io.Reader or io.Writer is accepted you can pass them.

Go
fmt.Fprintln(os.Stderr, "log diagnostico")
io.Copy(os.Stdout, os.Stdin)  // echo

Exercises

Exercise#go.m8.l3.e1
Attempts: 0Loading…

Print the first command-line argument (os.Args[1]). If it is missing, print 'manca argomento' and return.

Loading editor…

Solution available after 3 attempts

Exercise#go.m8.l3.e2
Attempts: 0Loading…

Write the string 'ciao' to the file 'out.txt' with os.WriteFile (permissions 0644). Handle the error.

Loading editor…

Solution available after 3 attempts

Quiz#go.m8.l3.e3
Ready

What is the index of the PROGRAM NAME in os.Args?

Go
fmt.Println(os.Args[???])
Answer options