encoding/csv
Package csv reads and writes comma-separated values (CSV) files. The package supports the format described in RFC 4180, with customizable delimiters and options.
Overview
The csv package provides:
- Reader - For reading CSV data from an io.Reader
- Writer - For writing CSV data to an io.Writer
A csv file contains zero or more records of one or more fields per record. Each record is separated by the newline character. White space is considered part of a field.
Types
Reader
type Reader struct {
Comma rune // Field delimiter (default ',')
Comment rune // Comment character (disabled by default)
FieldsPerRecord int // Number of expected fields per record
LazyQuotes bool // Allow lazy quotes
TrimLeadingSpace bool // Trim leading space
ReuseRecord bool // Reuse record slice for performance
}
A Reader reads records from a CSV-encoded file. As returned by NewReader, a Reader expects input conforming to RFC 4180.
NewReader
func NewReader(r io.Reader) *Reader
NewReader returns a new Reader that reads from r.
The reader to read CSV data from
A new Reader instance with Comma set to ’,’
Example:
in := `first_name,last_name,username
"Rob","Pike",rob
Ken,Thompson,ken
"Robert","Griesemer","gri"`
r := csv.NewReader(strings.NewReader(in))
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Println(record)
}
// Output:
// [first_name last_name username]
// [Rob Pike rob]
// [Ken Thompson ken]
// [Robert Griesemer gri]
Methods
Read
func (r *Reader) Read() (record []string, err error)
Read reads one record (a slice of fields) from r. If the record has an unexpected number of fields, Read returns the record along with the error ErrFieldCount.
A slice of strings representing the fields in the CSV record
Returns io.EOF when there are no more records. Returns ParseError for parsing errors. Returns ErrFieldCount if FieldsPerRecord is set and the record has the wrong number of fields.
ReadAll
func (r *Reader) ReadAll() (records [][]string, err error)
ReadAll reads all the remaining records from r. Each record is a slice of fields. A successful call returns err == nil, not err == io.EOF.
A slice of records, where each record is a slice of field strings
Returns nil on success, or an error if reading fails
Example:
in := `first_name,last_name,username
"Rob","Pike",rob
Ken,Thompson,ken
"Robert","Griesemer","gri"`
r := csv.NewReader(strings.NewReader(in))
records, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
fmt.Print(records)
// Output:
// [[first_name last_name username] [Rob Pike rob] [Ken Thompson ken] [Robert Griesemer gri]]
InputOffset
func (r *Reader) InputOffset() int64
InputOffset returns the input stream byte offset of the current reader position. The offset gives the location of the end of the most recently read row and the beginning of the next row.
Configuration Fields
Comma
The field delimiter. Set to ’,’ by NewReader. Must be a valid rune and must not be \r, \n, or the Unicode replacement character (0xFFFD).
Example:
in := `first_name;last_name;username
"Rob";"Pike";rob`
r := csv.NewReader(strings.NewReader(in))
r.Comma = ';' // Use semicolon as delimiter
Comment
If not 0, lines beginning with the Comment character without preceding whitespace are ignored. Must be a valid rune and must not be \r, \n, or the Unicode replacement character. It must also not be equal to Comma.
r := csv.NewReader(strings.NewReader(in))
r.Comment = '#' // Lines starting with # are ignored
FieldsPerRecord
The number of expected fields per record:
- If positive: Read requires each record to have the given number of fields
- If 0: Read sets it to the number of fields in the first record
- If negative: No check is made and records may have a variable number of fields
LazyQuotes
If true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field.
TrimLeadingSpace
If true, leading white space in a field is ignored, even if the field delimiter (Comma) is white space.
ReuseRecord
If true, calls to Read may return a slice sharing the backing array of the previous call’s returned slice for performance. By default, each call to Read returns newly allocated memory.
Writer
type Writer struct {
Comma rune // Field delimiter (default ',')
UseCRLF bool // Use \r\n as line terminator (default false)
}
A Writer writes records using CSV encoding. As returned by NewWriter, a Writer writes records terminated by a newline and uses ’,’ as the field delimiter.
NewWriter
func NewWriter(w io.Writer) *Writer
NewWriter returns a new Writer that writes to w.
The writer to write CSV data to
A new Writer instance with Comma set to ’,’
Example:
records := [][]string{
{"first_name", "last_name", "username"},
{"Rob", "Pike", "rob"},
{"Ken", "Thompson", "ken"},
{"Robert", "Griesemer", "gri"},
}
w := csv.NewWriter(os.Stdout)
for _, record := range records {
if err := w.Write(record); err != nil {
log.Fatalln("error writing record to csv:", err)
}
}
w.Flush()
if err := w.Error(); err != nil {
log.Fatal(err)
}
// Output:
// first_name,last_name,username
// Rob,Pike,rob
// Ken,Thompson,ken
// Robert,Griesemer,gri
Methods
Write
func (w *Writer) Write(record []string) error
Write writes a single CSV record to w along with any necessary quoting. A record is a slice of strings with each string being one field. Writes are buffered, so Flush must eventually be called to ensure that the record is written to the underlying io.Writer.
A slice of strings representing the fields to write
Returns an error if the delimiter is invalid or if writing fails
WriteAll
func (w *Writer) WriteAll(records [][]string) error
WriteAll writes multiple CSV records to w using Write and then calls Flush, returning any error from the Flush.
A slice of records to write, where each record is a slice of field strings
Returns any error from writing or flushing
Example:
records := [][]string{
{"first_name", "last_name", "username"},
{"Rob", "Pike", "rob"},
{"Ken", "Thompson", "ken"},
{"Robert", "Griesemer", "gri"},
}
w := csv.NewWriter(os.Stdout)
w.WriteAll(records) // calls Flush internally
if err := w.Error(); err != nil {
log.Fatalln("error writing csv:", err)
}
Flush
Flush writes any buffered data to the underlying io.Writer. To check if an error occurred during Flush, call Error.
Error
func (w *Writer) Error() error
Error reports any error that has occurred during a previous Write or Flush.
Configuration Fields
Comma
The field delimiter. Set to ’,’ by NewWriter. Must be a valid rune and must not be \r, \n, the Unicode replacement character (0xFFFD), or a quote character.
w := csv.NewWriter(os.Stdout)
w.Comma = ';' // Use semicolon as delimiter
UseCRLF
If true, the Writer ends each output line with \r\n instead of \n.
w := csv.NewWriter(os.Stdout)
w.UseCRLF = true // Use Windows-style line endings
Error Types
ParseError
type ParseError struct {
StartLine int // Line where the record starts
Line int // Line where the error occurred
Column int // Column (1-based byte index) where the error occurred
Err error // The actual error
}
A ParseError is returned for parsing errors. Line and column numbers are 1-indexed.
Error Variables
var ErrBareQuote = errors.New("bare \" in non-quoted-field")
var ErrQuote = errors.New("extraneous or missing \" in quoted-field")
var ErrFieldCount = errors.New("wrong number of fields")
These errors can be returned in ParseError.Err:
- ErrBareQuote - A bare quote appeared in an unquoted field
- ErrQuote - An extraneous or missing quote appeared in a quoted field
- ErrFieldCount - The record has the wrong number of fields
Field Quoting
Fields that start and stop with the quote character ” are called quoted-fields:
normal string,"quoted-field"
Within a quoted-field, a quote character followed by a second quote character is considered a single quote:
"the ""word"" is true","a ""quoted-field"""
Results in:
{`the "word" is true`, `a "quoted-field"`}
Multiline Fields
Newlines and commas may be included in a quoted-field:
"Multi-line
field","comma is ,"
Results in:
{`Multi-line
field`, `comma is ,`}