From 2265144de8655d2a298946a428457a334a9dee9d Mon Sep 17 00:00:00 2001 From: Daniil Medvedev <medvdanil@gmail.com> Date: Wed, 22 Jul 2015 15:08:00 +0300 Subject: [PATCH] csv style fix --- src/ffisyms.cc | 2 +- src/lib/csv/csv.c | 89 +++++++++++++++------------ src/lib/csv/csv.h | 28 +++++---- src/lua/csv.lua | 137 ++++++++++++++++++++++++------------------ test/app/csv.test.lua | 15 +++-- test/unit/csv.c | 26 ++++---- test/unit/csv.result | 14 +---- 7 files changed, 165 insertions(+), 146 deletions(-) diff --git a/src/ffisyms.cc b/src/ffisyms.cc index 77a764afce..095b4f4a97 100644 --- a/src/ffisyms.cc +++ b/src/ffisyms.cc @@ -70,7 +70,7 @@ void *ffi_symbols[] = { (void *) csv_create, (void *) csv_destroy, (void *) csv_setopt, - (void *) csv_iter_create, + (void *) csv_iterator_create, (void *) csv_next, (void *) csv_feed, }; diff --git a/src/lib/csv/csv.c b/src/lib/csv/csv.c index 9640a822e7..454b9d8bc8 100644 --- a/src/lib/csv/csv.c +++ b/src/lib/csv/csv.c @@ -27,6 +27,8 @@ csv_create(struct csv *csv) csv->csv_delim= ','; csv->csv_quote = '\"'; csv->csv_realloc = realloc; + csv->emit_field = csv_emit_field_empty; + csv->emit_row = csv_emit_row_empty; } void @@ -34,19 +36,28 @@ csv_destroy(struct csv *csv) { if(csv->buf) { csv->csv_realloc(csv->buf, 0); - csv->buf = 0; + csv->buf = NULL; } } -int csv_isvalid(struct csv *csv) +int +csv_isvalid(struct csv *csv) { if (csv->prevsymb == csv->csv_quote) { csv->state = csv->state == CSV_BUF_IN_QUOTES ? CSV_BUF_OUT_OF_QUOTES : CSV_BUF_IN_QUOTES; csv->prevsymb = ' '; } - csv->csv_invalid = csv->state == CSV_BUF_IN_QUOTES; - return !csv->csv_invalid; + if (csv->csv_error_status == CSV_ER_OK && csv->state == CSV_BUF_IN_QUOTES) + csv->csv_error_status = CSV_ER_INVALID; + return !csv->csv_error_status; +} + +int +csv_get_error_status(struct csv *csv) +{ + return csv->csv_error_status; } + void csv_setopt(struct csv *csv, int opt, ...) { @@ -60,12 +71,12 @@ csv_setopt(struct csv *csv, int opt, ...) csv->csv_quote = va_arg(args, int); break; case CSV_OPT_REALLOC: - csv->csv_realloc = va_arg(args, void*); + csv->csv_realloc = va_arg(args, void* (*)(void*, long unsigned int)); break; case CSV_OPT_EMIT_FIELD: - csv->emit_field = va_arg(args, void*); + csv->emit_field = va_arg(args, void (*)(void*, const char *, const char *)); case CSV_OPT_EMIT_ROW: - csv->emit_row = va_arg(args, void*); + csv->emit_row = va_arg(args, void (*)(void*)); case CSV_OPT_CTX: csv->emit_ctx = va_arg(args, void*); } @@ -76,12 +87,10 @@ const char * csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst) { if (end - s == 0) - return 0; + return NULL; assert(end - s > 0); - if (csv->emit_field == NULL) - csv->emit_field = csv_emit_field_empty; - if (csv->emit_row == NULL) - csv->emit_row = csv_emit_row_empty; + assert(csv->emit_field); + assert(csv->emit_row); const char *p = s; while(p != end) { @@ -89,7 +98,11 @@ csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst) if (csv->buf == 0 || (csv->bufp && csv->buf_len < csv->bufp - csv->buf + 1)) { csv->buf_len = (int)((csv->bufp - csv->buf + 1) * csv_buf_expand_factor + 1); - char *new_buf = csv->csv_realloc(csv->buf, csv->buf_len); + char *new_buf = (char *) csv->csv_realloc(csv->buf, csv->buf_len); + if(new_buf == NULL) { + csv->csv_error_status = CSV_ER_MEMORY_ERROR; + return NULL; + } csv->bufp = csv->bufp - csv->buf + new_buf; csv->buf = new_buf; } @@ -111,12 +124,12 @@ csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst) } csv->prevsymb = *p; switch(csv->state) { - case CSV_LEADING_SPACES: //leading spaces + case CSV_LEADING_SPACES: csv->bufp = csv->buf; if (*p != ' ') { csv->state = CSV_BUF_OUT_OF_QUOTES; } - else break; + else break; //spaces passed, perform field at once case CSV_BUF_OUT_OF_QUOTES: if (isendl || *p == csv->csv_delim) { csv->state = CSV_LEADING_SPACES; @@ -153,7 +166,7 @@ csv_parse_common(struct csv *csv, const char *s, const char *end, int onlyfirst) csv->bufp = 0; if(onlyfirst) { if(p + 1 == end) - return 0; + return NULL; else return p + 1; } @@ -183,15 +196,15 @@ csv_finish_parsing(struct csv *csv) } if (csv->buf) csv->csv_realloc(csv->buf, 0); - csv->bufp = 0; - csv->buf = 0; + csv->bufp = NULL; + csv->buf = NULL; csv->buf_len = 0; } } void -csv_iter_create(struct csv_iterator *it, struct csv *csv) +csv_iterator_create(struct csv_iterator *it, struct csv *csv) { memset(it, 0, sizeof(struct csv_iterator)); it->csv = csv; @@ -199,20 +212,19 @@ csv_iter_create(struct csv_iterator *it, struct csv *csv) int csv_next(struct csv_iterator *it) { - it->field = 0; + it->field = NULL; it->field_len = 0; - if(it->buf_begin == 0) + if(it->buf_begin == NULL) return CSV_IT_NEEDMORE; if(it->buf_begin == it->buf_end) { - if (!it->csv->csv_invalid && !csv_isvalid(it->csv)) { - it->csv->csv_invalid = 1; + if (!it->csv->csv_error_status && !csv_isvalid(it->csv)) { it->csv->csv_realloc(it->csv->buf, 0); - it->csv->buf = 0; - it->csv->bufp = 0; + it->csv->buf = NULL; + it->csv->bufp = NULL; it->csv->buf_len = 0; return CSV_IT_ERROR; } - if(it->csv->bufp == 0) { + if(it->csv->bufp == NULL) { return CSV_IT_EOF; } if(it->csv->state != CSV_END_OF_INPUT) { @@ -224,15 +236,17 @@ csv_next(struct csv_iterator *it) { return CSV_IT_OK; } else { it->csv->csv_realloc(it->csv->buf, 0); - it->csv->buf = 0; - it->csv->bufp = 0; + it->csv->buf = NULL; + it->csv->bufp = NULL; it->csv->buf_len = 0; return CSV_IT_EOL; } } const char *tail = csv_parse_common(it->csv, it->buf_begin, it->buf_end, 1); + if(csv_get_error_status(it->csv) == CSV_ER_MEMORY_ERROR) + return CSV_IT_ERROR; it->buf_begin = tail; - if(it->csv->bufp == 0 && it->csv->prevsymb) + if(it->csv->bufp == NULL && it->csv->prevsymb) return CSV_IT_EOL; if(tail == it->buf_end) return CSV_IT_NEEDMORE; @@ -242,13 +256,13 @@ csv_next(struct csv_iterator *it) { } void -csv_feed(struct csv_iterator *it, const char *str) +csv_feed(struct csv_iterator *it, const char *buf, size_t buf_len) { - it->buf_begin = str; - it->buf_end = str + strlen(str); + it->buf_begin = buf; + it->buf_end = buf + buf_len; } -int +size_t csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size) { char *p = dst; @@ -259,17 +273,14 @@ csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst } while(*field) { if(*field == csv->csv_quote) { - if(p - dst >= buf_size) - return -1; + assert(p - dst < buf_size); *p++ = csv->csv_quote; } - if(p - dst >= buf_size) - return -1; + assert(p - dst < buf_size); *p++ = *field++; } if(inquotes) { - if(p - dst >= buf_size) - return -1; + assert(p - dst < buf_size); *p++ = csv->csv_quote; } *p = 0; diff --git a/src/lib/csv/csv.h b/src/lib/csv/csv.h index 8faaadc85e..5b9854a4c2 100644 --- a/src/lib/csv/csv.h +++ b/src/lib/csv/csv.h @@ -19,7 +19,7 @@ struct csv char csv_quote; char prevsymb; - int csv_invalid; + int csv_error_status; int csv_ending_spaces; void *(*csv_realloc)(void*, size_t); @@ -30,8 +30,7 @@ struct csv size_t buf_len; }; -//parser options -enum { +enum parser_options { CSV_OPT_DELIMITER, CSV_OPT_QUOTE, CSV_OPT_REALLOC, @@ -40,8 +39,7 @@ enum { CSV_OPT_CTX }; -//iteraion states -enum { +enum iteraion_states { CSV_IT_OK, CSV_IT_EOL, CSV_IT_NEEDMORE, @@ -49,14 +47,20 @@ enum { CSV_IT_ERROR }; -//parser states -enum { CSV_LEADING_SPACES, +enum parser_states { + CSV_LEADING_SPACES, CSV_BUF_OUT_OF_QUOTES, CSV_BUF_IN_QUOTES, CSV_NEWLINE, CSV_END_OF_INPUT }; +enum error_status { + CSV_ER_OK, + CSV_ER_INVALID, + CSV_ER_MEMORY_ERROR +}; + void csv_create(struct csv *csv); @@ -84,10 +88,10 @@ void csv_finish_parsing(struct csv *csv); /** - * if quote not closed returns 0 + * @return 0 is ok */ int -csv_isvalid(struct csv *csv); +csv_get_error_status(struct csv *csv); /** * @brief The csv_iterator struct allows iterate field by field through csv @@ -103,7 +107,7 @@ struct csv_iterator { }; void -csv_iter_create(struct csv_iterator *it, struct csv *csv); +csv_iterator_create(struct csv_iterator *it, struct csv *csv); /** * Recieves next element from csv * element is field or end of line @@ -117,7 +121,7 @@ csv_next(struct csv_iterator *); * empty buffer means end of iteration */ void -csv_feed(struct csv_iterator *, const char *); +csv_feed(struct csv_iterator *it, const char *buf, size_t buf_len); /** * @brief csv_escape_field prepares field to out in file. @@ -125,7 +129,7 @@ csv_feed(struct csv_iterator *, const char *); * At worst escaped field will 2 times more symbols than input field. * @return length of escaped field or -1 if not enough space in buffer. */ -int +size_t csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size); diff --git a/src/lua/csv.lua b/src/lua/csv.lua index 0fd0b93edc..c06ba5ab3c 100644 --- a/src/lua/csv.lua +++ b/src/lua/csv.lua @@ -36,12 +36,10 @@ ffi.cdef[[ const char *field; size_t field_len; }; - typedef struct csv csv_t; - typedef struct csv_iterator csv_iterator_t; - void csv_iter_create(struct csv_iterator *it, struct csv *csv); + void csv_iterator_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 *csv, const char *field, size_t field_len, char *dst, size_t buf_size); + void csv_feed(struct csv_iterator *, const char *, size_t); + size_t csv_escape_field(struct csv *csv, const char *field, size_t field_len, char *dst, size_t buf_size); enum { CSV_IT_OK, CSV_IT_EOL, @@ -51,17 +49,6 @@ ffi.cdef[[ }; ]] -local make_readable = function(s) - rd = {} - rd.val = s - rd.read = function(self, cnt) - local res = self.val; - self.val = "" - return res - end - return rd -end - local make_writable = function() wr = {} wr.returnstring = "" @@ -81,12 +68,21 @@ local iter = function(csvstate, i) local st = ffi.C.csv_next(it) 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)) + if readable then + local buf = readable:read(csv_chunk_size) + ffi.C.csv_feed(it, buf, string.len(buf)) + else + ffi.C.csv_feed(it, "", 0) + end elseif st == ffi.C.CSV_IT_EOL then i = i + 1 - return i, tup + if i > 0 then + return i, tup + end elseif st == ffi.C.CSV_IT_OK then - table.insert(tup, ffi.string(it[0].field, it[0].field_len)) + if i >= 0 then + tup[#tup + 1] = ffi.string(it.field, it.field_len) + end elseif st == ffi.C.CSV_IT_ERROR then log.warn("CSV file has errors") break @@ -100,69 +96,94 @@ end local module = {} -module.delimiter = ',' -module.quote = '"' - --@brief parse csv string by string --@param readable must be string or object with method read(num) returns string ---@param csv_chunk_size (default 4096). Parser will read by csv_chunk_size symbols +--@param opts.chunk_size (default 4096). Parser will read by chunk_size symbols +--@param opts.delimiter (default ','). +--@param opts.quote (default '"'). +--@param opts.skip_head_lines (default 0). Skip header. --@return iter function, iterator state -module.iterate = function(readable, csv_chunk_size) - csv_chunk_size = csv_chunk_size or 4096 - if type(readable) == "string" then - readable = make_readable(readable) +module.iterate = function(readable, opts) + opts = opts or {} + if not opts.chunk_size then + opts.chunk_size = 4096 end - if type(readable.read) ~= "function" then - error("Usage: load(object with method read(num) returns string)") + if not opts.delimiter then + opts.delimiter = ',' + end + if not opts.quote then + opts.quote = '"' + end + if not opts.skip_head_lines then + opts.skip_head_lines = 0 + end + if type(readable) ~= "string" and type(readable.read) ~= "function" then + error("Usage: load(string or object with method read(num) returns string)") + end + local str + if type(readable) == "string" then + str = readable + readable = null + else + str = readable:read(opts.chunk_size) end - - local str = readable:read(csv_chunk_size) if not str then - error("Usage: load(object with method read(num) returns string)") + error("Usage: load(string or object with method read(num) returns string)") end - local it = ffi.new('csv_iterator_t[1]') - local csv = ffi.new('csv_t[1]') + local it = ffi.new('struct csv_iterator') + local csv = ffi.new('struct csv') ffi.C.csv_create(csv) - csv[0].csv_delim = string.byte(module.delimiter) - csv[0].csv_quote = string.byte(module.quote) - ffi.C.csv_iter_create(it, csv) - ffi.C.csv_feed(it, str) - - return iter, {readable, csv_chunk_size, csv, it}, 0 + ffi.gc(csv, ffi.C.csv_destroy) + + csv.csv_delim = string.byte(opts.delimiter) + csv.csv_quote = string.byte(opts.quote) + ffi.C.csv_iterator_create(it, csv) + ffi.C.csv_feed(it, str, string.len(str)) + + return iter, {readable, opts.chunk_size, csv, it}, -opts.skip_head_lines end --@brief parse csv and make table ---@param skip_lines is number of lines to skip. --@return table -module.load = function(readable, skip_lines, csv_chunk_size) - skip_lines = skip_lines or 0 - csv_chunk_size = csv_chunk_size or 4096 +module.load = function(readable, opts) + opts = opts or {} result = {} - for i, tup in module.iterate(readable, csv_chunk_size) do - if i > skip_lines then - result[i - skip_lines] = tup - end + for i, tup in module.iterate(readable, opts) do + result[i] = tup end - return result end --@brief dumps tuple or table as csv +--@param t is tuple or table --@param writable must be object with method write(string) like file or socket +--@param opts.delimiter (default ','). +--@param opts.quote (default '"'). --@return there is no writable it returns csv as string -module.dump = function(t, writable) +module.dump = function(t, opts, writable) + opts = opts or {} + writable = writable or null + if not opts.delimiter then + opts.delimiter = ',' + end + if not opts.quote then + opts.quote = '"' + end + if type(writable) == "nil" then writable = make_writable() end if type(writable.write) ~= "function" or type(t) ~= "table" then - error("Usage: dump(writable, table)") + error("Usage: dump(table[, opts, writable])") end - local csv = ffi.new('csv_t[1]') + local csv = ffi.new('struct csv') ffi.C.csv_create(csv) - csv[0].csv_delim = string.byte(module.delimiter) - csv[0].csv_quote = string.byte(module.quote) + ffi.gc(csv, ffi.C.csv_destroy) + csv.csv_delim = string.byte(opts.delimiter) + csv.csv_quote = string.byte(opts.quote) + local bufsz = 256 - local buf = csv[0].csv_realloc(ffi.cast(ffi.typeof('void *'), 0), bufsz) + local buf = csv.csv_realloc(ffi.cast(ffi.typeof('void *'), 0), bufsz) if type(t[1]) ~= 'table' then t = {t} end @@ -172,20 +193,20 @@ module.dump = function(t, writable) strf = tostring(field) if (strf:len() + 1) * 2 > bufsz then bufsz = (strf:len() + 1) * 2 - buf = csv[0].csv_realloc(buf, bufsz) + buf = csv.csv_realloc(buf, bufsz) end local len = ffi.C.csv_escape_field(csv, strf, string.len(strf), buf, bufsz) if first then first = false else - writable:write(module.delimiter) + writable:write(opts.delimiter) end writable:write(ffi.string(buf, len)) end writable:write('\n') end ffi.C.csv_destroy(csv) - csv[0].csv_realloc(buf, 0) + csv.csv_realloc(buf, 0) if writable.returnstring then return writable.returnstring end diff --git a/test/app/csv.test.lua b/test/app/csv.test.lua index 06ebaefec4..720eabd3d4 100755 --- a/test/app/csv.test.lua +++ b/test/app/csv.test.lua @@ -41,11 +41,11 @@ test:is(table2str(csv.load(readable)), test1, "obj test1") readable.v = ", ,\n , \n\n" readable.i = 0 -test:is(table2str(csv.load(readable, 0, 1)), test2, "obj test2") +test:is(table2str(csv.load(readable, {chunk_size = 1} )), test2, "obj test2") readable.v = ", \r\nkp\"\"v" readable.i = 0 -test:is(table2str(csv.load(readable, 0, 3)), test3, "obj test3") +test:is(table2str(csv.load(readable, {chunk_size = 3})), test3, "obj test3") tmpdir = fio.tempdir() file1 = fio.pathjoin(tmpdir, 'file.1') @@ -57,7 +57,7 @@ f:write("123 , 5 , 92 , 0, 0\n" .. "1, 12 34, 56, \"quote , \", 66\nok") f:close() f = fio.open(file1, {'O_RDONLY'}) -test:is(table2str(csv.load(f,0,10)), test4, "fio test1") +test:is(table2str(csv.load(f, {chunk_size = 10})), test4, "fio test1") f:close() @@ -72,14 +72,13 @@ f:write("1\n23,456,abcac,\'multiword field 4\'\n" .. ) f:close() f = fio.open(file2, {'O_RDONLY'}) -test:is(table2str(csv.load(f, 0, 1)), test5, "fio test2") --symbol by symbol reading +test:is(table2str(csv.load(f, {chunk_size = 1})), test5, "fio test2") --symbol by symbol reading f:close() f = fio.open(file2, {'O_RDONLY'}) -test:is(table2str(csv.load(f, 1, 7)), test6, "fio test3") --7 symbols per chunk +test:is(table2str(csv.load(f, {chunk_size = 7, skip_head_lines = 1})), test6, "fio test3") --7 symbols per chunk f:close() - t = { {'quote" d', ',and, comma', 'both " of " t,h,e,m'}, {'"""', ',","'}, @@ -90,10 +89,10 @@ t = { } f = require("fio").open(file3, { "O_WRONLY", "O_TRUNC" , "O_CREAT"}, 0x1FF) -csv.dump(t, f) +csv.dump(t, {}, f) f:close() f = fio.open(file3, {'O_RDONLY'}) -t2 = csv.load(f, 0, 5) +t2 = csv.load(f, {chunk_size = 5}) f:close() test:is(table2str(t), table2str(t2), "test roundtrip") diff --git a/test/unit/csv.c b/test/unit/csv.c index 6863254967..ce7561c855 100644 --- a/test/unit/csv.c +++ b/test/unit/csv.c @@ -106,7 +106,7 @@ void test5() { csv_setopt(&csv, CSV_OPT_DELIMITER, '\t'); csv_parse_chunk(&csv, s, s + strlen(s)); csv_finish_parsing(&csv); - printf("valid: %s\n", csv.csv_invalid ? "NO" : "yes"); + printf("valid: %s\n", csv.csv_error_status == CSV_ER_INVALID ? "NO" : "yes"); csv_destroy(&csv); footer(); } @@ -123,7 +123,7 @@ void test6() { csv_parse_chunk(&csv, s2, s2 + 2); csv_parse_chunk(&csv, s2 + 2, s2 + strlen(s2)); csv_finish_parsing(&csv); - printf("valid: %s\n", csv.csv_invalid ? "NO" : "yes"); + printf("valid: %s\n", csv_get_error_status(&csv) == CSV_ER_INVALID ? "NO" : "yes"); csv_destroy(&csv); footer(); } @@ -218,7 +218,7 @@ void random_generated_test() { csv_parse_chunk(&csv, rand_test, rand_test + strlen(rand_test)); csv_finish_parsing(&csv); printf("line_cnt=%d, fieldsizes_cnt=%d\n", (int)cnt.line_cnt, (int)cnt.fieldsizes_cnt); - printf("valid: %s\n", csv.csv_invalid ? "NO" : "yes"); + printf("valid: %s\n", csv_get_error_status(&csv) == CSV_ER_INVALID ? "NO" : "yes"); csv_destroy(&csv); footer(); @@ -229,13 +229,13 @@ void iter_test1() { struct csv_iterator it; struct csv csv; csv_create(&csv); - csv_iter_create(&it, &csv); + csv_iterator_create(&it, &csv); int st = 0; const char *buf = ",d ,e\r\n12,42,3\no\n"; while((st = csv_next(&it)) != CSV_IT_EOF) { switch(st) { case CSV_IT_NEEDMORE: - csv_feed(&it, buf); + csv_feed(&it, buf, strlen(buf)); buf += strlen(buf); break; case CSV_IT_EOL: @@ -258,14 +258,14 @@ void iter_test2() { struct csv_iterator it; struct csv csv; csv_create(&csv); - csv_iter_create(&it, &csv); + csv_iterator_create(&it, &csv); int st = 0; const char ar[] = {'1', '\n', 0, '2', '3', 0, 0}; const char *buf = ar; while((st = csv_next(&it)) != CSV_IT_EOF) { switch(st) { case CSV_IT_NEEDMORE: - csv_feed(&it, buf); + csv_feed(&it, buf, strlen(buf)); buf += 3; break; case CSV_IT_EOL: @@ -286,18 +286,14 @@ void iter_test2() { void csv_out() { header(); - const char fields[5][24] = { "abc", "with,comma", "\"in quotes\"", "1 \" quote", - "long field, return \"-1\"" }; - char buf[24]; + const char fields[4][24] = { "abc", "with,comma", "\"in quotes\"", "1 \" quote"}; + char buf[54]; int i; struct csv csv; csv_create(&csv); - for(i = 0; i < 5; i++) { + for(i = 0; i < 4; i++) { int len = csv_escape_field(&csv, fields[i], strlen(fields[i]), buf, sizeof(buf)); - if(len != -1) - printf("%s<len=%d>%c", buf, len, i == 4 ? '\n' : ','); - else - printf("<len=%d>%c", len, i == 4 ? '\n' : ','); + printf("%s<len=%d>%c", buf, len, i == 3 ? '\n' : ','); } footer(); diff --git a/test/unit/csv.result b/test/unit/csv.result index 61a66cb1a0..d27ee8f7a6 100644 --- a/test/unit/csv.result +++ b/test/unit/csv.result @@ -3,24 +3,20 @@ || |1| |2| |3| |123| -valid: yes *** test1: done *** *** test2 *** |123| |456| |abcac| |'multiword field 4'| |none| |none| |0| || || || || || || -valid: yes *** test2: done *** *** test3 *** |1| || |2| -valid: yes *** test3: done *** *** test4 *** |123| |5| |92| |0| |0| |1| |12 34| |56| |quote , | |66| |ok| -valid: yes *** test4: done *** *** test5 *** |abc| |longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong| |0| @@ -51,24 +47,20 @@ valid: yes *** common_test *** |first| |last| |address| |city| |zip| |John| |Doe| |120 any st.| |Anytown, WW| |08123| -valid: yes *** common_test: done *** *** common_test *** |a| |b| |c| |1| |"| |"| |2| |3| |4| -valid: yes *** common_test: done *** *** common_test *** |a| |b| |1| |ha "ha" ha| |3| |4| -valid: yes *** common_test: done *** *** common_test *** |key| |val| |1| |{"type": "Point", "coordinates": [102.0, 0.5]}| -valid: yes *** common_test: done *** *** common_test *** |a| |b| |c| @@ -76,7 +68,6 @@ valid: yes |Once upon a time| |5| |6| |7| |8| |9| -valid: yes *** common_test: done *** *** common_test *** |a| |b| @@ -84,17 +75,14 @@ valid: yes "ha" ha| |3| |4| -valid: yes *** common_test: done *** *** common_test *** |a| |b| |c| |1| |2| |3| |4| |5| |а нет ли ошибок?| -valid: yes *** common_test: done *** *** common_test *** |www| |aaa| |tt | -valid: yes *** common_test: done *** *** iter_test1 *** || |d| |e| @@ -106,6 +94,6 @@ valid: yes |23| *** iter_test2: done *** *** csv_out *** -abc<len=3>,"with,comma"<len=12>,""in quotes""<len=13>,1 "" quote<len=10>,<len=-1> +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