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

csv string input/output\n documentation on text format

parent f05029b6
No related branches found
No related tags found
No related merge requests found
Tarantool supports CSV file input/output.
CSV is comma separated values, like this:
example.txt:
package,method,return value
fio,pathjoin,string
csv,load,table
none,",comma in field", and ""quote""
Commas in fields must be in quotes
If there are quotes in a field, it must be double-quotes.
You can set delimiter and quote character:
csv.delimiter = ','
csv.quote = '"'
csv.iterate = function(readable[, csv_chunk_size])
--@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
--@return iter function, iterator state
Example:
f = require("fio").open("example.txt", { "O_RDONLY"})
for tup in csv.iterate(f) do
print(tup[1], tup[2], tup[3])
end
Output:
package method return value
fio pathjoin string
csv load table
none ,comma in field and "quote"
csv.load = function(readable, csv_chunk_size)
--@brief parse csv and make table
--@return table
csv.dump = function(t, writable)
--@brief dumps tuple or table as csv
--@param writable must be object with method write(string) like file or socket
--@return there is no writable it returns csv as string
\ No newline at end of file
......@@ -75,64 +75,112 @@ local iter = function(csvstate)
end
end
local csv = {
iterate = function(readable, csv_chunk_size)
csv_chunk_size = csv_chunk_size or 4096
if type(readable.read) ~= "function" then
error("Usage: load(object with read method)")
end
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 it = ffi.new('csv_iterator_t[1]')
local csv = ffi.new('csv_t[1]')
ffi.C.csv_create(csv)
ffi.C.csv_iter_create(it, csv)
return iter, {readable, csv_chunk_size, csv, it}
end,
load = function(readable, csv_chunk_size)
csv_chunk_size = csv_chunk_size or 4096
if type(readable.read) ~= "function" then
error("Usage: load(object with read method)")
end
local make_writable = function()
wr = {}
wr.returnstring = ""
wr.write = function(self, s)
wr.returnstring = wr.returnstring .. s
end
return wr
end
result = {}
for tup in csv.iterate(readable, csv_chunk_size) do
table.insert(result, tup)
end
local module = {}
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)
if type(t[1]) ~= 'table' then
t = {t}
end
for k, line in pairs(t) do
local 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))
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
--@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)
end
if type(readable.read) ~= "function" then
error("Usage: load(object with method read(num) returns string)")
end
local str = readable:read(csv_chunk_size)
if not str then
error("Usage: load(object with method read(num) returns string)")
end
local it = ffi.new('csv_iterator_t[1]')
local csv = ffi.new('csv_t[1]')
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}
end
--@brief parse csv and make table
--@return table
module.load = function(readable, csv_chunk_size)
csv_chunk_size = csv_chunk_size or 4096
result = {}
for tup in module.iterate(readable, csv_chunk_size) do
table.insert(result, tup)
end
return result
end
--@brief dumps tuple or table as csv
--@param writable must be object with method write(string) like file or socket
--@return there is no writable it returns csv as string
module.dump = function(t, writable)
if type(writable) == "nil" then
writable = make_writable()
end
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)
csv[0].csv_delim = string.byte(module.delimiter)
csv[0].csv_quote = string.byte(module.quote)
local bufsz = 256
local buf = csv[0].csv_realloc(ffi.cast(ffi.typeof('void *'), 0), bufsz)
if type(t[1]) ~= 'table' then
t = {t}
end
for k, line in pairs(t) do
local 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(module.delimiter)
end
writable:write('\n')
writable:write(ffi.string(buf, len))
end
csv[0].csv_realloc(buf, 0)
writable:write('\n')
end
}
return csv
csv[0].csv_realloc(buf, 0)
if writable.returnstring then
return writable.returnstring
end
end
return module
......@@ -51,3 +51,18 @@ aba
local res = internal.pwrite(self.fh
iflag = bit.bor(iflag
test str dump:
quote"" d,",and, comma","both "" of "" t,h,e,m"
"""""",","","""
"mul
ti
li
ne
",field
""
"
"
test load(dump(t)): true
......@@ -84,7 +84,7 @@ t = {
}
f = require("fio").open(file3, { "O_WRONLY", "O_TRUNC" , "O_CREAT"}, 0x1FF)
csv.dump(f, t)
csv.dump(t, f)
f:close()
f = fio.open(file3, {'O_RDONLY'})
t2 = csv.load(f, 5)
......@@ -100,6 +100,9 @@ for tup in csv.iterate(f) do
end
f:close()
print("test str dump:")
print(csv.dump(t))
print("test load(dump(t)): " .. tostring(table2str(t) == table2str(csv.load(csv.dump(t)))))
fio.unlink(file1)
fio.unlink(file2)
......
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