From ec193ca63999451abea20592a60804f85781cc57 Mon Sep 17 00:00:00 2001 From: Daniil Medvedev <medvdanil@gmail.com> Date: Fri, 17 Jul 2015 11:48:33 +0300 Subject: [PATCH] csv output(with tests) --- src/lib/csv/csv.c | 23 +++++++++++++- src/lib/csv/csv.h | 15 +++++----- src/lua/csv.lua | 71 +++++++++++++++++++++++++++++++------------- test/unit/csv.c | 18 +++++++++++ test/unit/csv.result | 3 ++ 5 files changed, 102 insertions(+), 28 deletions(-) diff --git a/src/lib/csv/csv.c b/src/lib/csv/csv.c index d4856b97f0..25a8050eb4 100644 --- a/src/lib/csv/csv.c +++ b/src/lib/csv/csv.c @@ -242,7 +242,28 @@ csv_next(struct csv_iterator *it) { } void -csv_feed(struct csv_iterator *it, const char *str) { +csv_feed(struct csv_iterator *it, const char *str) +{ it->buf_begin = str; it->buf_end = str + strlen(str); } + +int +csv_escape_field(struct csv *csv, const char *field, char *dst) +{ + char *p = dst; + int inquotes = 0; + if(strchr(field, csv->csv_delim) || strchr(field, '\n') || strchr(field, '\r')) { + inquotes = 1; + *p++ = csv->csv_quote; + } + while(*field) { + if(*field == csv->csv_quote) + *p++ = csv->csv_quote; + *p++ = *field++; + } + if(inquotes) + *p++ = csv->csv_quote; + *p = 0; + return p - dst; +} diff --git a/src/lib/csv/csv.h b/src/lib/csv/csv.h index bfb699e645..fb2e9376a6 100644 --- a/src/lib/csv/csv.h +++ b/src/lib/csv/csv.h @@ -81,13 +81,6 @@ csv_parse_chunk(struct csv *csv, const char *s, const char *end); void csv_finish_parsing(struct csv *csv); -/** - * Format variadic arguments and print them into - * a stream, adding CSV markup. - */ -int -csv_snprintf(struct csv *csv, FILE *f, const char *format, ...); - /** * if quote not closed returns 0 */ @@ -122,6 +115,14 @@ csv_next(struct csv_iterator *); void csv_feed(struct csv_iterator *, const char *); +/** + * @brief csv_escape_field adds pair quote and + * if there is comma in field, adds surrounding quotes + */ + +int +csv_escape_field(struct csv *csv, const char *field, char *dst); + #define CSV_ITERATOR_GET_FIELD(it) it->field #define CSV_ITERATOR_GET_FLEN(it) it->field_len diff --git a/src/lua/csv.lua b/src/lua/csv.lua index 13f437ce84..92ac72a334 100644 --- a/src/lua/csv.lua +++ b/src/lua/csv.lua @@ -41,20 +41,21 @@ ffi.cdef[[ void csv_iter_create(struct csv_iterator *it, struct csv *csv); int csv_next(struct csv_iterator *); void csv_feed(struct csv_iterator *, const char *); + int csv_escape_field(struct csv *, const char *, char *); enum { - CSV_IT_OK, - CSV_IT_EOL, - CSV_IT_NEEDMORE, - CSV_IT_EOF, - CSV_IT_ERROR + CSV_IT_OK, + CSV_IT_EOL, + CSV_IT_NEEDMORE, + CSV_IT_EOF, + CSV_IT_ERROR }; ]] csv = { -loadcsv = function(readable, csv_chunk_size) +load = function(readable, csv_chunk_size) csv_chunk_size = csv_chunk_size or 4096 if type(readable.read) ~= "function" then - error("Usage: loadcsv(object with read method)") + error("Usage: load(object with read method)") end local log = require('log') @@ -66,23 +67,53 @@ loadcsv = function(readable, csv_chunk_size) local tup = {} local result = {} while st ~= ffi.C.CSV_IT_EOF do - if st == ffi.C.CSV_IT_NEEDMORE then - ffi.C.csv_feed(it, readable:read(csv_chunk_size)) - elseif st == ffi.C.CSV_IT_EOL then - table.insert(result, tup) - tup = {} - elseif st == ffi.C.CSV_IT_OK then - table.insert(tup, ffi.string(it[0].field, it[0].field_len)) - elseif st == ffi.C.CSV_IT_ERROR then - log.warn("CSV file has errors") - elseif st == ffi.C.CSV_IT_EOF then - break - end - st = ffi.C.csv_next(it) + if st == ffi.C.CSV_IT_NEEDMORE then + ffi.C.csv_feed(it, readable:read(csv_chunk_size)) + elseif st == ffi.C.CSV_IT_EOL then + table.insert(result, tup) + tup = {} + elseif st == ffi.C.CSV_IT_OK then + table.insert(tup, ffi.string(it[0].field, it[0].field_len)) + elseif st == ffi.C.CSV_IT_ERROR then + log.warn("CSV file has errors") + elseif st == ffi.C.CSV_IT_EOF then + break + end + st = ffi.C.csv_next(it) end return result end +, +dump = function(writable, t) + if type(writable.write) ~= "function" or type(t) ~= "table" then + error("Usage: dump(writable, table)") + end + local csv = ffi.new('csv_t[1]') + ffi.C.csv_create(csv) + local bufsz = 256 + --local buf = ffi.new('char[?]', bufsz) + local buf = csv[0].csv_realloc(ffi.cast(ffi.typeof('void *'), 0), bufsz) + for k, line in pairs(t) do + first = true + for k2, field in pairs(line) do + strf = tostring(field) + if (strf:len() + 1) * 2 > bufsz then + bufsz = (strf:len() + 1) * 2 + buf = csv[0].csv_realloc(buf, bufsz) + end + local len = ffi.C.csv_escape_field(csv, strf, buf) + if first then + first = false + else + writable:write(',') + end + writable:write(ffi.string(buf, len)) + end + writable:write('\n') + end + csv[0].csv_realloc(buf, 0) +end } return csv diff --git a/test/unit/csv.c b/test/unit/csv.c index 2386568f46..8a36c7b6b6 100644 --- a/test/unit/csv.c +++ b/test/unit/csv.c @@ -247,6 +247,22 @@ void iter_test2() { footer(); } +void csv_out() { + header(); + + const char fields[4][36] = { "abc", "with,comma", "\"in quotes\"", "1 \" quote"}; + char buf[18]; + int i; + struct csv csv; + csv_create(&csv); + for(i = 0; i < 4; i++) { + int len = csv_escape_field(&csv, fields[i], buf); + printf("%s<len=%d>%c", buf, len, i == 3 ? '\n' : ','); + } + + footer(); +} + int main() { test1(); test2(); @@ -314,5 +330,7 @@ int main() { iter_test1(); iter_test2(); + //output test + csv_out(); return 0; } diff --git a/test/unit/csv.result b/test/unit/csv.result index 9e43199617..8d56fa9744 100644 --- a/test/unit/csv.result +++ b/test/unit/csv.result @@ -183,4 +183,7 @@ valid: yes |1| |23| *** iter_test2: done *** + *** csv_out *** +abc<len=3>,"with,comma"<len=12>,""in quotes""<len=13>,1 "" quote<len=10> + *** csv_out: done *** \ No newline at end of file -- GitLab