github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/encoding/csv/writer.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package csv 6 7 import ( 8 "bufio" 9 "io" 10 "strings" 11 "unicode" 12 "unicode/utf8" 13 ) 14 15 // A Writer writes records to a CSV encoded file. 16 // 17 // As returned by NewWriter, a Writer writes records terminated by a 18 // newline and uses ',' as the field delimiter. The exported fields can be 19 // changed to customize the details before the first call to Write or WriteAll. 20 // 21 // Comma is the field delimiter. 22 // 23 // If UseCRLF is true, the Writer ends each record with \r\n instead of \n. 24 type Writer struct { 25 Comma rune // Field delimiter (set to ',' by NewWriter) 26 UseCRLF bool // True to use \r\n as the line terminator 27 w *bufio.Writer 28 } 29 30 // NewWriter returns a new Writer that writes to w. 31 func NewWriter(w io.Writer) *Writer { 32 return &Writer{ 33 Comma: ',', 34 w: bufio.NewWriter(w), 35 } 36 } 37 38 // Writer writes a single CSV record to w along with any necessary quoting. 39 // A record is a slice of strings with each string being one field. 40 func (w *Writer) Write(record []string) (err error) { 41 for n, field := range record { 42 if n > 0 { 43 if _, err = w.w.WriteRune(w.Comma); err != nil { 44 return 45 } 46 } 47 48 // If we don't have to have a quoted field then just 49 // write out the field and continue to the next field. 50 if !w.fieldNeedsQuotes(field) { 51 if _, err = w.w.WriteString(field); err != nil { 52 return 53 } 54 continue 55 } 56 if err = w.w.WriteByte('"'); err != nil { 57 return 58 } 59 60 for _, r1 := range field { 61 switch r1 { 62 case '"': 63 _, err = w.w.WriteString(`""`) 64 case '\r': 65 if !w.UseCRLF { 66 err = w.w.WriteByte('\r') 67 } 68 case '\n': 69 if w.UseCRLF { 70 _, err = w.w.WriteString("\r\n") 71 } else { 72 err = w.w.WriteByte('\n') 73 } 74 default: 75 _, err = w.w.WriteRune(r1) 76 } 77 if err != nil { 78 return 79 } 80 } 81 82 if err = w.w.WriteByte('"'); err != nil { 83 return 84 } 85 } 86 if w.UseCRLF { 87 _, err = w.w.WriteString("\r\n") 88 } else { 89 err = w.w.WriteByte('\n') 90 } 91 return 92 } 93 94 // Flush writes any buffered data to the underlying io.Writer. 95 // To check if an error occurred during the Flush, call Error. 96 func (w *Writer) Flush() { 97 w.w.Flush() 98 } 99 100 // Error reports any error that has occurred during a previous Write or Flush. 101 func (w *Writer) Error() error { 102 _, err := w.w.Write(nil) 103 return err 104 } 105 106 // WriteAll writes multiple CSV records to w using Write and then calls Flush. 107 func (w *Writer) WriteAll(records [][]string) (err error) { 108 for _, record := range records { 109 err = w.Write(record) 110 if err != nil { 111 return err 112 } 113 } 114 return w.w.Flush() 115 } 116 117 // fieldNeedsQuotes returns true if our field must be enclosed in quotes. 118 // Empty fields, files with a Comma, fields with a quote or newline, and 119 // fields which start with a space must be enclosed in quotes. 120 func (w *Writer) fieldNeedsQuotes(field string) bool { 121 if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { 122 return true 123 } 124 125 r1, _ := utf8.DecodeRuneInString(field) 126 return unicode.IsSpace(r1) 127 }