I/O#
Go’s I/O surface is built around two interfaces: io.Reader
and io.Writer. Files, network sockets, byte buffers,
compressed streams, and HTTP request bodies all satisfy them,
so the same helpers (io.Copy, io.ReadAll,
bufio.Scanner) work everywhere. os covers the process
surface; encoding/json, encoding/binary, and fmt
cover serialisation and formatting.
For networking I/O, see Networking. For CLI argument handling, see CLI.
Files#
os.ReadFile and os.WriteFile are the one-shot path.
os.Open and os.Create give a *os.File for streaming.
import "os"
data, err := os.ReadFile("config.json")
if err != nil { return err }
err = os.WriteFile("out.txt", []byte("hello\n"), 0o644)
if err != nil { return err }
f, err := os.Open("input.log")
if err != nil { return err }
defer f.Close()
out, err := os.Create("output.log")
if err != nil { return err }
defer out.Close()
Readers and writers#
io.Reader, io.Writer, io.Closer, and their
combinations (io.ReadWriteCloser) are the operator’s main
abstraction.
import "io"
n, err := io.Copy(dst, src) // copy any reader to any writer
data, err := io.ReadAll(r) // drain a reader
r := io.LimitReader(src, 1024) // cap at 1 KB
tee := io.TeeReader(src, log) // split a stream
A function should accept the smallest interface it needs;
io.Reader covers most read-only callers.
func summarize(r io.Reader) (int, error) {
data, err := io.ReadAll(r)
if err != nil { return 0, err }
return len(data), nil
}
Buffered I/O#
bufio wraps a reader / writer with a buffer. bufio.Scanner
gives line- or token-iteration; bufio.Reader exposes the
ReadString / ReadBytes family.
import "bufio"
f, _ := os.Open("input.log")
defer f.Close()
sc := bufio.NewScanner(f)
sc.Buffer(make([]byte, 0, 1<<20), 1<<20) // raise the line cap
for sc.Scan() {
line := sc.Text()
if strings.Contains(line, "error") {
fmt.Println(line)
}
}
if err := sc.Err(); err != nil { return err }
For writes, bufio.Writer plus defer w.Flush().
w := bufio.NewWriter(out)
defer w.Flush()
for _, item := range items {
fmt.Fprintln(w, item)
}
Standard streams#
os.Stdin, os.Stdout, os.Stderr are *os.File
values. fmt.Println writes to stdout; fmt.Fprintln lets
the operator pick the writer.
fmt.Fprintln(os.Stderr, "warning:", msg)
sc := bufio.NewScanner(os.Stdin)
for sc.Scan() {
fmt.Println(strings.ToUpper(sc.Text()))
}
JSON#
encoding/json covers the typical case. The operator marshals
to a struct (with tags) for shape-driven serialisation;
map[string]any for dynamic data.
import "encoding/json"
type Config struct {
Host string `json:"host"`
Port int `json:"port"`
}
var c Config
if err := json.Unmarshal(data, &c); err != nil { return err }
out, err := json.MarshalIndent(c, "", " ")
if err != nil { return err }
For streaming, json.Decoder and json.Encoder.
dec := json.NewDecoder(r)
for {
var item Item
if err := dec.Decode(&item); err == io.EOF { break } else if err != nil { return err }
handle(item)
}
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
for _, item := range items {
if err := enc.Encode(item); err != nil { return err }
}
Binary#
encoding/binary handles fixed-format binary payloads with
explicit endianness.
import "encoding/binary"
var port uint16
if err := binary.Read(r, binary.BigEndian, &port); err != nil { return err }
if err := binary.Write(w, binary.LittleEndian, header); err != nil { return err }
Strings, bytes, builders#
strings.Reader and bytes.Buffer adapt strings / byte
slices to the reader / writer interfaces. strings.Builder is
the operator’s quadratic-free string concatenator.
r := strings.NewReader("hello")
io.Copy(os.Stdout, r)
var b bytes.Buffer
b.Write(header); b.Write(payload)
data := b.Bytes()
var sb strings.Builder
for i := 0; i < 100; i++ { fmt.Fprintf(&sb, "%d ", i) }
out := sb.String()
Formatting (fmt)#
fmt is the operator’s printf. Verbs: %v (default),
%+v (struct with field names), %#v (Go syntax),
%s / %q, %d / %x, %f / %g, %w
(wrap an error), %T (type), %t (bool).
fmt.Printf("%-10s %5d %.2f%%\n", "cpu", 12, 12.345)
fmt.Sprintf("%+v", user) // {Name:rk Age:30}
fmt.Errorf("loadConfig %q: %w", path, err)
Paths#
path/filepath for filesystem paths (handles OS separator);
path for forward-slash paths (URL-style).
import "path/filepath"
p := filepath.Join("/etc", "app", "config.json")
base := filepath.Base(p) // "config.json"
dir := filepath.Dir(p) // "/etc/app"
abs, err := filepath.Abs("relative/path")
Process and env#
os.Getenv, os.Setenv, os.Getwd, os.Exit.
os/exec for running subprocesses.
home := os.Getenv("HOME")
cmd := exec.Command("ip", "-j", "addr")
out, err := cmd.Output() // captures stdout
if err != nil { return err }
cmd2 := exec.CommandContext(ctx, "long-job")
cmd2.Stdout, cmd2.Stderr = os.Stdout, os.Stderr
err = cmd2.Run()
References#
Types for
stringand[]bytesemantics.CLI for the CLI argument surface and exit codes.
Networking for socket I/O.