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

Think of a CSV file like a very strict, old-school filing system. Imagine you have a physical ledger where every page is a row, and every entry is separated by a vertical line. If you wanted to move that data into a modern database, you wouldn't just read the page as one long string of text; you'd use a template to slice the page into specific columns so you know exactly which piece of text is the "Customer Name" and which is the "Order Total."

In Go, the encoding/csv package is that template. While it's tempting to just use strings.Split(line, ","), I'm telling you right now: don't do it. Real-world CSVs are messy. They have quoted strings that contain commas inside them (like "New York, NY"), and strings.Split will tear that right in half, ruining your data. The csv package handles those edge cases for you.

Turning a Text File into Slices

When you're reading a CSV, you're essentially turning a stream of bytes into a series of string slices ([]string). Each slice represents one row. Here is how I usually set this up when I'm parsing a list of server assets:

import (
    "encoding/csv"
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("servers.csv")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    reader := csv.NewReader(file)
    
    // ReadAll is great for small files, but for huge ones, 
    // you'd want to use reader.Read() in a loop.
    records, err := reader.ReadAll()
    if err != nil {
        panic(err)
    }

    for _, row := range records {
        fmt.Printf("Hostname: %s, IP: %s\n", row[0], row[1])
    }
}

Notice how ReadAll() gives us a [][]string. It's a slice of slices. If your file is massive—say, a 2GB export from a database—ReadAll() will eat all your RAM. In those cases, I use a for loop with reader.Read() to process the file one line at a time.

Dumping Data Back to Disk

Writing is the mirror image of reading. You provide the csv.Writer with a slice of strings, and it handles the quoting and comma-placement for you. I've seen plenty of devs forget the most important part of the writing process: the flush.

func writeInventory(filename string, data [][]string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    writer := csv.NewWriter(file)
    
    // WriteAll handles the looping and the flushing for you.
    // If you use writer.Write() manually, you MUST call writer.Flush().
    if err := writer.WriteAll(data); err != nil {
        return err
    }

    return nil
}

The Flush Trap

If you aren't using WriteAll and you're instead calling writer.Write(row) inside a loop, the data isn't written to the file immediately. It's stored in a buffer to make the process faster. If your program exits or the function returns before you call writer.Flush(), you'll end up with a truncated or completely empty file. It's a classic "where did my data go?" bug that usually takes ten minutes of staring at the code to find.

One last tip: if you encounter a file that uses semicolons (;) instead of commas—which happens a lot with European Excel exports—you don't need a different package. Just set reader.Comma = ';' after initializing your reader.




📋 Practical Task

Build a Product Price Inflator

You have been given a CSV file named products.csv with the following content:

ProductName,Price
Laptop,1200.00
Mouse,25.50
Keyboard,75.00
Monitor,300.00

Write a Go program that does the following:

  • Reads products.csv.
  • Skips the header row.
  • Parses the price (the second column) as a float.
  • Increases the price by 10%.
  • Writes the updated data (including the header) to a new file called updated_products.csv.

Make sure you handle the string-to-float conversion using strconv.ParseFloat and format the new price back to two decimal places using fmt.Sprintf.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.