...
1
2
3
4
5 package csv
6
7 import (
8 "bufio"
9 "io"
10 "strings"
11 "unicode"
12 "unicode/utf8"
13 )
14
15
16
17
18
19
20
21
22
23
24 type Writer struct {
25 Comma rune
26 UseCRLF bool
27 w *bufio.Writer
28 }
29
30
31 func NewWriter(w io.Writer) *Writer {
32 return &Writer{
33 Comma: ',',
34 w: bufio.NewWriter(w),
35 }
36 }
37
38
39
40 func (w *Writer) Write(record []string) error {
41 if !validDelim(w.Comma) {
42 return errInvalidDelim
43 }
44
45 for n, field := range record {
46 if n > 0 {
47 if _, err := w.w.WriteRune(w.Comma); err != nil {
48 return err
49 }
50 }
51
52
53
54 if !w.fieldNeedsQuotes(field) {
55 if _, err := w.w.WriteString(field); err != nil {
56 return err
57 }
58 continue
59 }
60
61 if err := w.w.WriteByte('"'); err != nil {
62 return err
63 }
64 for len(field) > 0 {
65
66 i := strings.IndexAny(field, "\"\r\n")
67 if i < 0 {
68 i = len(field)
69 }
70
71
72 if _, err := w.w.WriteString(field[:i]); err != nil {
73 return err
74 }
75 field = field[i:]
76
77
78 if len(field) > 0 {
79 var err error
80 switch field[0] {
81 case '"':
82 _, err = w.w.WriteString(`""`)
83 case '\r':
84 if !w.UseCRLF {
85 err = w.w.WriteByte('\r')
86 }
87 case '\n':
88 if w.UseCRLF {
89 _, err = w.w.WriteString("\r\n")
90 } else {
91 err = w.w.WriteByte('\n')
92 }
93 }
94 field = field[1:]
95 if err != nil {
96 return err
97 }
98 }
99 }
100 if err := w.w.WriteByte('"'); err != nil {
101 return err
102 }
103 }
104 var err error
105 if w.UseCRLF {
106 _, err = w.w.WriteString("\r\n")
107 } else {
108 err = w.w.WriteByte('\n')
109 }
110 return err
111 }
112
113
114
115 func (w *Writer) Flush() {
116 w.w.Flush()
117 }
118
119
120 func (w *Writer) Error() error {
121 _, err := w.w.Write(nil)
122 return err
123 }
124
125
126 func (w *Writer) WriteAll(records [][]string) error {
127 for _, record := range records {
128 err := w.Write(record)
129 if err != nil {
130 return err
131 }
132 }
133 return w.w.Flush()
134 }
135
136
137
138
139
140
141
142
143
144
145
146
147
148 func (w *Writer) fieldNeedsQuotes(field string) bool {
149 if field == "" {
150 return false
151 }
152 if field == `\.` || strings.ContainsRune(field, w.Comma) || strings.ContainsAny(field, "\"\r\n") {
153 return true
154 }
155
156 r1, _ := utf8.DecodeRuneInString(field)
157 return unicode.IsSpace(r1)
158 }
159
View as plain text