go 55 lines · 7 steps

Parsing INI files in Go

A line-by-line scanner turns INI text into a nested map of sections and key-value pairs.

Explained by highlit
1package config
2 
3import (
4 "bufio"
5 "fmt"
6 "io"
7 "strings"
8)
9 
10type INI map[string]map[string]string
11 
12func ParseINI(r io.Reader) (INI, error) {
13 result := INI{}
14 section := ""
15 result[section] = map[string]string{}
16 
17 scanner := bufio.NewScanner(r)
18 for line := 1; scanner.Scan(); line++ {
19 text := strings.TrimSpace(scanner.Text())
20 if text == "" || strings.HasPrefix(text, ";") || strings.HasPrefix(text, "#") {
21 continue
22 }
23 
24 if strings.HasPrefix(text, "[") {
25 if !strings.HasSuffix(text, "]") {
26 return nil, fmt.Errorf("line %d: unterminated section header", line)
27 }
28 section = strings.TrimSpace(text[1 : len(text)-1])
29 if section == "" {
30 return nil, fmt.Errorf("line %d: empty section name", line)
31 }
32 if _, ok := result[section]; !ok {
33 result[section] = map[string]string{}
34 }
35 continue
36 }
37 
38 key, value, ok := strings.Cut(text, "=")
39 if !ok {
40 return nil, fmt.Errorf("line %d: expected key=value, got %q", line, text)
41 }
42 
43 key = strings.TrimSpace(key)
44 value = strings.Trim(strings.TrimSpace(value), `"'`)
45 if key == "" {
46 return nil, fmt.Errorf("line %d: empty key", line)
47 }
48 result[section][key] = value
49 }
50 
51 if err := scanner.Err(); err != nil {
52 return nil, fmt.Errorf("read config: %w", err)
53 }
54 return result, nil
55}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A single mutable `section` variable acts as the parser's state, routing each key into the right map.
  2. 2Validating structure eagerly and returning errors with line numbers makes malformed input easy to diagnose.
  3. 3Seeding a default empty section lets keys appear before any header without special-casing.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing INI files in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code