← Back to all posts

Three Input Parsing Mistakes

AoC:25:06:1Godebugginginput-parsing

Day 6's "cephalopod math worksheet" taught me three painful lessons about input parsing. All from the same puzzle.

Mistake 1: Assuming Uniformity From Test Input

Treating test input as the specification instead of one example.

My Gloomy Reality

The test input looked beautifully uniform:

123 328  51 64 
 45 64  387 23 
  6 98  215 314
*   +   *   +

Four columns. Each exactly 4 characters wide. My brain immediately latched onto this pattern: "Ah, columns are fixed-width! I'll just slice the string every 4 characters."

Then I ran it on the real input:

79 338  2   956 3311 92  29 93 4  878 76 988 3    1  76 8   758 ...
 84 921  4  2238 3478 849 36 65 1  359 57 673 6333 5  55 62  846 ...
562 5154 8  2186    1 769 41 59 49 371 77 927 5961 15 45 798 961 ...
*   +    +  +    +    *   +  *  *  +   +  *   +    +  *  *   +   ...

Variable-width columns. Some are 2 characters, some are 4, some are 5. My hardcoded "slice every 4 chars" approach was completely wrong.

Mistake 2: Line-Wrapping Illusion

Your terminal wraps long lines. 5 rows can look like 50+.

My Gloomy Reality

When I first opened the real input, my terminal showed what looked like dozens of rows of numbers. I started panicking—"wait, the problem said there's only one operator row at the bottom, but I see numbers everywhere!"

Turns out the input was just 5 rows. But each row was ~1800 characters wide, and my terminal was wrapping them into visual chaos.

I wasted time trying to understand a "complex multi-row structure" that didn't exist.

Mistake 3: Premature Input Transformation

Don't TrimSpace, ToLower, or transform input until you understand WHY.

My Gloomy Reality

My first instinct was to clean up the input:

// ❌ "Let me just clean this up..."
line = strings.TrimSpace(line)

This destroyed the column alignment. The leading spaces in 79 338 2 ... weren't noise—they were data. They indicated that position 0 was part of a column, and the number 79 was right-aligned within it.

By trimming, I shifted all the numbers left and broke the positional relationship with the operator row.

The Insight

All three mistakes had the same root cause: I was imposing structure instead of reading structure.

The breakthrough came when I stopped looking at the numbers and started looking at the operators:

*   +    +  +    +    *   +  *  *  +   ...

The operators weren't evenly spaced! Each operator marks the start of its column, and the spaces after it define that column's width. The operator row is the column specification.

Once I understood that, everything clicked:

The Fix

First, understand the input before touching it:

wc -l input                    # 5 rows, not 50
head -1 input | wc -c          # 1847 chars wide
less -S input                  # view without wrapping

Then, discover structure from the data itself:

// ❌ Before: hardcoded assumptions
columnWidth := 4  // "every column is 4 chars, right?"
line = strings.TrimSpace(line)  // "whitespace is noise, right?"

// ✅ After: discover columns from the operator row
func findColumns(opRow string) []Column {
    var cols []Column
    i := 0
    for i < len(opRow) {
        if opRow[i] == ' ' {
            i++
            continue
        }
        op := rune(opRow[i])
        start := i
        i++
        for i < len(opRow) && opRow[i] == ' ' {
            i++
        }
        cols = append(cols, Column{Op: op, Start: start, End: i})
    }
    return cols
}

Lessons Learned

1. Test input is ONE example, not THE specification. The test exists to show the rules, not the scale or edge cases.

2. Always wc -l before you trust your eyes. Your terminal lies. Line-wrapping creates phantom structure.

3. Parse raw first, transform only what you KNOW needs transforming. Whitespace might be data. Don't TrimSpace until you're sure it's noise.

4. Look for structure in the data, don't impose structure on the data. The operator row told me exactly where the columns were—I just wasn't listening.


Original puzzle: Advent of Code 2025 Day 6