Skip to content

Instantly share code, notes, and snippets.

@wperron
Created October 11, 2023 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wperron/0102d0b7f1d95d6f26596db62f04425f to your computer and use it in GitHub Desktop.
Save wperron/0102d0b7f1d95d6f26596db62f04425f to your computer and use it in GitHub Desktop.
Parse a csv file and export a Markdown table
// md-fmt takes a csv or tsv input file and outputs a formatted markdown table
// with the data.
package main
import (
"encoding/csv"
"flag"
"fmt"
"log"
"os"
"strings"
)
var (
sourcePath = flag.String("source", "", "path to the input file")
separator = flag.String("sep", ",", "separator character to use when reading the csv file")
lazyQuotes = flag.Bool("lazy-quotes", false, "controls the lazy-quotes setting on the csv reader")
)
func main() {
flag.Parse()
fd, err := os.Open(*sourcePath)
if err != nil {
log.Fatalf("failed to open source file %s: %s\n", *sourcePath, err)
}
sep := []rune(*separator)[0]
read := csv.NewReader(fd)
read.Comma = sep
read.TrimLeadingSpace = true
read.LazyQuotes = *lazyQuotes
records, err := read.ReadAll()
if err != nil {
log.Fatalf("failed to load records in memory: %s\n", err)
}
widths := make([]int, len(records[0]))
for _, row := range records {
for i, col := range row {
widths[i] = max(widths[i], len(col))
}
}
c := make([]string, len(records[0]))
log.Println(records[0], len(c))
for i := 0; i < len(c); i++ {
c[i] = " %-*s "
}
pattern := fmt.Sprintf("|%s|\n", strings.Join(c, "|"))
log.Println(pattern)
sb := strings.Builder{}
// Format header row
curr := make([]any, 0, 2*len(widths))
for i := range widths {
curr = append(curr, widths[i], records[0][i])
}
sb.WriteString(fmt.Sprintf(pattern, curr...))
// Format header separator row
curr = curr[:0] // empty slice but preserve capacity
for i := range widths {
curr = append(curr, widths[i], strings.Repeat("-", widths[i]))
}
sb.WriteString(fmt.Sprintf(pattern, curr...))
// Format rest of records
for i := 1; i < len(records); i++ {
curr = curr[:0]
for j := range widths {
curr = append(curr, widths[j], records[i][j])
}
sb.WriteString(fmt.Sprintf(pattern, curr...))
}
fmt.Print(sb.String())
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment