Skip to Content
Course content

164: Practice Exercise: Building a Config File Parser

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this exact bug in a dozen different code reviews over the years. Someone is tasked with writing a quick configuration parser—something to read a .env or a config.txt file—and they write a "simple" loop. It looks fine during the first test, but then it crashes the moment a developer adds a comment or a trailing space to the file.

func parseConfig(lines []string) map[string]string {
    config := make(map[string]string)
    for _, line := range lines {
        parts := strings.Split(line, "=")
        config[parts[0]] = parts[1]
    }
    return config
}

The Panic and the Messy Strings

There are three things happening here that will make your life miserable in production. First, if there's an empty line at the end of your file, strings.Split returns a slice with one element. When you try to access parts[1], Go will panic with an index out of range error. Your whole app just died because of a blank line.

Second, look at the keys. If your config file has PORT = 8080, your map key isn't "PORT"; it's "PORT ". You'll spend an hour debugging why config["PORT"] is returning an empty string, only to realize there's a hidden space in the key.

Finally, what happens if the value itself contains an equals sign? Imagine a connection string like DB_URL=host=localhost;port=5432. strings.Split will chop that into three pieces, and you'll lose everything after the second equals sign.

Splitting with Intent and Cleaning the Edges

To fix this, we need to be defensive. We should ignore lines that don't actually contain data, and we need to limit how many times we split the string. I always use strings.SplitN for this. By setting the limit to 2, we tell Go: "I only care about the first equals sign; leave the rest of the string alone."

Here is how I would actually write this logic:

func parseConfig(lines []string) map[string]string {
    config := make(map[string]string)
    for _, line := range lines {
        line = strings.TrimSpace(line)
        
        // Skip empty lines or comments
        if line == "" || strings.HasPrefix(line, "#") {
            continue
        }

        parts := strings.SplitN(line, "=", 2)
        if len(parts) != 2 {
            // You could return an error here, but skipping malformed 
            // lines is often more resilient for simple parsers.
            continue
        }

        key := strings.TrimSpace(parts[0])
        val := strings.TrimSpace(parts[1])
        config[key] = val
    }
    return config
}

The strings.TrimSpace calls are the real heroes here. They ensure that whether the user wrote KEY=VAL or KEY = VAL , your application sees the exact same data. I've also added a check for #, because any config file without comments is a file that will eventually be confusing to the person maintaining it.




📋 Practical Task

Build a Robust Environment Variable Loader

Your task is to create a utility that reads a simulated configuration file (a slice of strings) and converts it into a usable Go map. Your implementation must be resilient enough to handle "dirty" input.

Requirements:

  • Create a function ParseEnv(input []string) map[string]string.
  • Ignore any lines that are entirely whitespace.
  • Ignore any lines that start with # (comments), even if there is leading whitespace before the #.
  • Trim leading and trailing whitespace from both the key and the value.
  • Support values that contain equals signs (e.g., SECRET_KEY=abc=123=xyz should result in a value of "abc=123=xyz").
  • Skip lines that do not contain an equals sign without crashing the program.

Test your code with this input:

input := []string{
    "  # Database Settings  ",
    "DB_HOST = localhost ",
    "DB_PASS = password=with=equals",
    " ", 
    "  PORT=5432",
    "INVALID_LINE_NO_EQUALS",
    "  # End of file",
}

Your final map should contain exactly: {"DB_HOST": "localhost", "DB_PASS": "password=with=equals", "PORT": "5432"}.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.