Go trae un lector y un escritor de CSV en la biblioteca estándar, y se encargan solos de lo que suele salir mal a mano: las comillas, los separadores dentro de un campo y los saltos de línea. No hace falta ninguna dependencia.
También te puede interesar
Escribir
func escribir(ruta string, filas []Fila) error {
f, err := os.Create(ruta)
if err != nil {
return err
}
defer f.Close()
w := csv.NewWriter(f)
defer w.Flush()
if err := w.Write([]string{"nombre", "ciudad", "unidades"}); err != nil {
return err
}
for _, fi := range filas {
reg := []string{fi.Nombre, fi.Ciudad, strconv.Itoa(fi.Unidades)}
if err := w.Write(reg); err != nil {
return err
}
}
return w.Error()
}
clientes.csvnombre,ciudad,unidades Ana,Madrid,12 Luis,Lima,7 Marta,"Bogotá; centro",23
Fíjate en la última fila: el valor llevaba un punto y coma y el escritor lo entrecomilló solo. Eso es justo lo que se estropea cuando uno pega las comas a mano.
Flush no es opcional. El escritor acumula en memoria, así que sin Flush() el archivo puede quedarse vacío o a medias. Y como Flush no devuelve error, hay que preguntarlo después con w.Error(): ahí es donde aparece un disco lleno.Leer el archivo entero
f, err := os.Open("clientes.csv")
if err != nil {
return err
}
defer f.Close()
r := csv.NewReader(f)
registros, err := r.ReadAll()
if err != nil {
return err
}
total := 0
for _, reg := range registros[1:] { // [1:] se salta la cabecera
u, err := strconv.Atoi(reg[2])
if err != nil {
fmt.Println("fila mal formada:", reg)
continue
}
total += u
fmt.Printf("%-6s %-16s %3d\n", reg[0], reg[1], u)
}
fmt.Println("total de unidades:", total)
salidaAna Madrid 12 Luis Lima 7 Marta Bogotá; centro 23 total de unidades: 42
Cada registro es un []string: el CSV no tiene tipos, así que los números hay que convertirlos. strconv.Atoi devuelve también un error, y conviene usarlo en vez de ignorarlo: una celda vacía o con un texto es lo más común en los archivos reales.
Fila a fila, para los archivos grandes
ReadAll carga todo en memoria. Con un archivo de varios gigabytes hay que ir leyendo:
r := csv.NewReader(f)
r.Comma = ';' // separador distinto
r.FieldsPerRecord = 2 // exige exactamente dos campos
if _, err := r.Read(); err != nil { // descartar la cabecera
return err
}
for {
reg, err := r.Read()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
fmt.Println("error en una fila:", err)
continue
}
fmt.Printf("%s vive en %s\n", reg[0], reg[1])
}
salidaAna vive en Madrid Luis vive en Lima
El bucle termina cuando Read devuelve io.EOF. Nótese que se comprueba con errors.Is y no con ==: funciona igual aunque el error venga envuelto.
Cuando una fila está mal
Con FieldsPerRecord puesto, el lector avisa en vez de devolver datos descuadrados:
salidarecord on line 2: wrong number of fields
Dice la línea exacta. Y como el error es de esa fila y no del archivo, se puede saltar con continue y seguir procesando el resto, que es casi siempre lo que quieres.
Las opciones que vas a tocar
| Campo | Para qué |
|---|---|
r.Comma = ';' |
Cambiar el separador. Muy habitual con archivos de Excel en español |
r.Comment = '#' |
Ignorar las líneas que empiecen por ese carácter |
r.FieldsPerRecord |
Exigir un número fijo de campos. Con -1 acepta cualquiera |
r.TrimLeadingSpace |
Quitar los espacios tras el separador |
w.Comma |
Lo mismo, al escribir |
Todo el código se compiló y ejecutó con Go 1.25 antes de publicarla; las salidas y el contenido del archivo están copiados de esa ejecución. Documentación oficial: paquete encoding/csv.