encoding.csv: add write support

pull/1633/head
joe-conigliaro 2019-08-17 22:51:20 +10:00 committed by Alexander Medvednikov
parent 56566ba3d0
commit 1ba701e036
2 changed files with 114 additions and 12 deletions

View File

@ -1,9 +1,9 @@
module csv
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module csv
// Once interfaces are further along the idea would be to have something similar to
// go's io.reader & bufio.reader rather than reading the whole file into string, this
// would then satisfy that interface. I designed it this way to be easily adapted.
@ -21,6 +21,7 @@ struct Reader {
// has_header bool
// headings []string
data string
pub:
mut:
delimiter byte
comment byte
@ -92,6 +93,9 @@ fn (r mut Reader) read_record() ?[]string {
if r.delimiter == r.comment {
return err_comment_is_delim
}
if !valid_delim(r.delimiter) {
return err_invalid_delim
}
mut line := ''
for {
l := r.read_line() or {
@ -122,16 +126,18 @@ fn (r mut Reader) read_record() ?[]string {
else {
line = line.right(1)
i = line.index('"')
if i+1 == line.len {
// last record
fields << line.left(i)
break
}
next := line[i+1]
if next == r.delimiter {
fields << line.left(i)
line = line.right(i)
continue
if i != -1 {
if i+1 == line.len {
// last record
fields << line.left(i)
break
}
next := line[i+1]
if next == r.delimiter {
fields << line.left(i)
line = line.right(i)
continue
}
}
line = line.right(1)
}
@ -142,3 +148,10 @@ fn (r mut Reader) read_record() ?[]string {
return fields
}
fn valid_delim(b byte) bool {
return b != 0 &&
b != `"` &&
b != `\r` &&
b != `\n`
}

View File

@ -0,0 +1,89 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module csv
import strings
struct Writer {
sb strings.Builder
pub:
mut:
use_crlf bool
delimiter byte
}
pub fn new_writer() *Writer {
return &Writer{
delimiter: `,`,
sb: strings.new_builder(200)
}
}
// write writes a single record
pub fn (w mut Writer) write(record []string) ?bool {
if !valid_delim(w.delimiter) {
return err_invalid_delim
}
le := if w.use_crlf { '\r\n' } else { '\n' }
for n, _field in record {
mut field := _field
if n > 0 {
w.sb.write(w.delimiter.str())
}
if !w.field_needs_quotes(field) {
w.sb.write(field)
continue
}
w.sb.write('"')
for field.len > 0 {
mut i := field.index_any('"\r\n')
if i < 0 {
i = field.len
}
w.sb.write(field.left(i))
field = field.right(i)
if field.len > 0 {
z := field[0]
switch z {
case `"`:
w.sb.write('""')
case `\r` || `\n`:
w.sb.write(le)
}
field = field.right(1)
}
}
w.sb.write('"')
}
w.sb.write(le)
return true
}
// Once we have multi dimensional array
// pub fn (w &Writer) write_all(records [][]string) {
// for _, record in records {
// w.write(record)
// }
// }
fn (w &Writer) field_needs_quotes(field string) bool {
if field == '' {
return false
}
if field.contains(w.delimiter.str()) || (field.index_any('"\r\n') != -1) {
return true
}
return false
}
pub fn (w &Writer) str() string {
return w.sb.str()
}