| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package csv |
| |
| import ( |
| "bufio" |
| "io" |
| "os" |
| "strings" |
| "unicode" |
| "utf8" |
| ) |
| |
| // A Writer writes records to a CSV encoded file. |
| // |
| // As returned by NewWriter, a Writer writes records terminated by a |
| // newline and uses ',' as the field delimiter. The exported fields can be |
| // changed to customize the details before the first call to Write or WriteAll. |
| // |
| // Comma is the field delimiter. |
| // |
| // If UseCRLF is true, the Writer ends each record with \r\n instead of \n. |
| type Writer struct { |
| Comma int // Field delimiter (set to to ',' by NewWriter) |
| UseCRLF bool // True to use \r\n as the line terminator |
| w *bufio.Writer |
| } |
| |
| // NewWriter returns a new Writer that writes to w. |
| func NewWriter(w io.Writer) *Writer { |
| return &Writer{ |
| Comma: ',', |
| w: bufio.NewWriter(w), |
| } |
| } |
| |
| // Writer 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. |
| func (w *Writer) Write(record []string) (err os.Error) { |
| for n, field := range record { |
| if n > 0 { |
| if _, err = w.w.WriteRune(w.Comma); err != nil { |
| return |
| } |
| } |
| |
| // If we don't have to have a quoted field then just |
| // write out the field and continue to the next field. |
| if !w.fieldNeedsQuotes(field) { |
| if _, err = w.w.WriteString(field); err != nil { |
| return |
| } |
| continue |
| } |
| if err = w.w.WriteByte('"'); err != nil { |
| return |
| } |
| |
| for _, rune := range field { |
| switch rune { |
| case '"': |
| _, err = w.w.WriteString(`""`) |
| case '\r': |
| if !w.UseCRLF { |
| err = w.w.WriteByte('\r') |
| } |
| case '\n': |
| if w.UseCRLF { |
| _, err = w.w.WriteString("\r\n") |
| } else { |
| err = w.w.WriteByte('\n') |
| } |
| default: |
| _, err = w.w.WriteRune(rune) |
| } |
| if err != nil { |
| return |
| } |
| } |
| |
| if err = w.w.WriteByte('"'); err != nil { |
| return |
| } |
| } |
| if w.UseCRLF { |
| _, err = w.w.WriteString("\r\n") |
| } else { |
| err = w.w.WriteByte('\n') |
| } |
| return |
| } |
| |
| // Flush writes any buffered data to the underlying io.Writer. |
| func (w *Writer) Flush() { |
| w.w.Flush() |
| } |
| |
| // WriteAll writes multiple CSV records to w using Write and then calls Flush. |
| func (w *Writer) WriteAll(records [][]string) (err os.Error) { |
| for _, record := range records { |
| err = w.Write(record) |
| if err != nil { |
| break |
| } |
| } |
| w.Flush() |
| return nil |
| } |
| |
| // fieldNeedsQuotes returns true if our field must be enclosed in quotes. |
| // Empty fields, files with a Comma, fields with a quote or newline, and |
| // fields which start with a space must be enclosed in quotes. |
| func (w *Writer) fieldNeedsQuotes(field string) bool { |
| if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { |
| return true |
| } |
| |
| rune, _ := utf8.DecodeRuneInString(field) |
| return unicode.IsSpace(rune) |
| } |