...
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
25
26
27
28
29
30 type Writer struct {
31 Comma rune
32 UseCRLF bool
33 w *bufio.Writer
34 }
35
36
37 func NewWriter(w io.Writer) *Writer {
38 return &Writer{
39 Comma: ',',
40 w: bufio.NewWriter(w),
41 }
42 }
43
44
45
46
47
48 func (w *Writer) Write(record []string) error {
49 if !validDelim(w.Comma) {
50 return errInvalidDelim
51 }
52
53 for n, field := range record {
54 if n > 0 {
55 if _, err := w.w.WriteRune(w.Comma); err != nil {
56 return err
57 }
58 }
59
60
61
62 if !w.fieldNeedsQuotes(field) {
63 if _, err := w.w.WriteString(field); err != nil {
64 return err
65 }
66 continue
67 }
68
69 if err := w.w.WriteByte('"'); err != nil {
70 return err
71 }
72 for len(field) > 0 {
73
74 i := strings.IndexAny(field, "\"\r\n")
75 if i < 0 {
76 i = len(field)
77 }
78
79
80 if _, err := w.w.WriteString(field[:i]); err != nil {
81 return err
82 }
83 field = field[i:]
84
85
86 if len(field) > 0 {
87 var err error
88 switch field[0] {
89 case '"':
90 _, err = w.w.WriteString(`""`)
91 case '\r':
92 if !w.UseCRLF {
93 err = w.w.WriteByte('\r')
94 }
95 case '\n':
96 if w.UseCRLF {
97 _, err = w.w.WriteString("\r\n")
98 } else {
99 err = w.w.WriteByte('\n')
100 }
101 }
102 field = field[1:]
103 if err != nil {
104 return err
105 }
106 }
107 }
108 if err := w.w.WriteByte('"'); err != nil {
109 return err
110 }
111 }
112 var err error
113 if w.UseCRLF {
114 _, err = w.w.WriteString("\r\n")
115 } else {
116 err = w.w.WriteByte('\n')
117 }
118 return err
119 }
120
121
122
123 func (w *Writer) Flush() {
124 w.w.Flush()
125 }
126
127
128 func (w *Writer) Error() error {
129 _, err := w.w.Write(nil)
130 return err
131 }
132
133
134
135 func (w *Writer) WriteAll(records [][]string) error {
136 for _, record := range records {
137 err := w.Write(record)
138 if err != nil {
139 return err
140 }
141 }
142 return w.w.Flush()
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156
157 func (w *Writer) fieldNeedsQuotes(field string) bool {
158 if field == "" {
159 return false
160 }
161
162 if field == `\.` {
163 return true
164 }
165
166 if w.Comma < utf8.RuneSelf {
167 for i := 0; i < len(field); i++ {
168 c := field[i]
169 if c == '\n' || c == '\r' || c == '"' || c == byte(w.Comma) {
170 return true
171 }
172 }
173 } else {
174 if strings.ContainsRune(field, w.Comma) || strings.ContainsAny(field, "\"\r\n") {
175 return true
176 }
177 }
178
179 r1, _ := utf8.DecodeRuneInString(field)
180 return unicode.IsSpace(r1)
181 }
182
View as plain text