Skip to content
Snippets Groups Projects
Commit ec193ca6 authored by Daniil Medvedev's avatar Daniil Medvedev
Browse files

csv output(with tests)

parent 1ef1c4d0
No related branches found
No related tags found
No related merge requests found
...@@ -242,7 +242,28 @@ csv_next(struct csv_iterator *it) { ...@@ -242,7 +242,28 @@ csv_next(struct csv_iterator *it) {
} }
void 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_begin = str;
it->buf_end = str + strlen(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;
}
...@@ -81,13 +81,6 @@ csv_parse_chunk(struct csv *csv, const char *s, const char *end); ...@@ -81,13 +81,6 @@ csv_parse_chunk(struct csv *csv, const char *s, const char *end);
void void
csv_finish_parsing(struct csv *csv); 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 * if quote not closed returns 0
*/ */
...@@ -122,6 +115,14 @@ csv_next(struct csv_iterator *); ...@@ -122,6 +115,14 @@ csv_next(struct csv_iterator *);
void void
csv_feed(struct csv_iterator *, const char *); 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_FIELD(it) it->field
#define CSV_ITERATOR_GET_FLEN(it) it->field_len #define CSV_ITERATOR_GET_FLEN(it) it->field_len
......
...@@ -41,20 +41,21 @@ ffi.cdef[[ ...@@ -41,20 +41,21 @@ ffi.cdef[[
void csv_iter_create(struct csv_iterator *it, struct csv *csv); void csv_iter_create(struct csv_iterator *it, struct csv *csv);
int csv_next(struct csv_iterator *); int csv_next(struct csv_iterator *);
void csv_feed(struct csv_iterator *, const char *); void csv_feed(struct csv_iterator *, const char *);
int csv_escape_field(struct csv *, const char *, char *);
enum { enum {
CSV_IT_OK, CSV_IT_OK,
CSV_IT_EOL, CSV_IT_EOL,
CSV_IT_NEEDMORE, CSV_IT_NEEDMORE,
CSV_IT_EOF, CSV_IT_EOF,
CSV_IT_ERROR CSV_IT_ERROR
}; };
]] ]]
csv = { csv = {
loadcsv = function(readable, csv_chunk_size) load = function(readable, csv_chunk_size)
csv_chunk_size = csv_chunk_size or 4096 csv_chunk_size = csv_chunk_size or 4096
if type(readable.read) ~= "function" then if type(readable.read) ~= "function" then
error("Usage: loadcsv(object with read method)") error("Usage: load(object with read method)")
end end
local log = require('log') local log = require('log')
...@@ -66,23 +67,53 @@ loadcsv = function(readable, csv_chunk_size) ...@@ -66,23 +67,53 @@ loadcsv = function(readable, csv_chunk_size)
local tup = {} local tup = {}
local result = {} local result = {}
while st ~= ffi.C.CSV_IT_EOF do while st ~= ffi.C.CSV_IT_EOF do
if st == ffi.C.CSV_IT_NEEDMORE then if st == ffi.C.CSV_IT_NEEDMORE then
ffi.C.csv_feed(it, readable:read(csv_chunk_size)) ffi.C.csv_feed(it, readable:read(csv_chunk_size))
elseif st == ffi.C.CSV_IT_EOL then elseif st == ffi.C.CSV_IT_EOL then
table.insert(result, tup) table.insert(result, tup)
tup = {} tup = {}
elseif st == ffi.C.CSV_IT_OK then elseif st == ffi.C.CSV_IT_OK then
table.insert(tup, ffi.string(it[0].field, it[0].field_len)) table.insert(tup, ffi.string(it[0].field, it[0].field_len))
elseif st == ffi.C.CSV_IT_ERROR then elseif st == ffi.C.CSV_IT_ERROR then
log.warn("CSV file has errors") log.warn("CSV file has errors")
elseif st == ffi.C.CSV_IT_EOF then elseif st == ffi.C.CSV_IT_EOF then
break break
end end
st = ffi.C.csv_next(it) st = ffi.C.csv_next(it)
end end
return result return result
end 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 return csv
......
...@@ -247,6 +247,22 @@ void iter_test2() { ...@@ -247,6 +247,22 @@ void iter_test2() {
footer(); 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() { int main() {
test1(); test1();
test2(); test2();
...@@ -314,5 +330,7 @@ int main() { ...@@ -314,5 +330,7 @@ int main() {
iter_test1(); iter_test1();
iter_test2(); iter_test2();
//output test
csv_out();
return 0; return 0;
} }
...@@ -183,4 +183,7 @@ valid: yes ...@@ -183,4 +183,7 @@ valid: yes
|1| |1|
|23| |23|
*** iter_test2: done *** *** 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment