Rename noQuotes to onlyRequiredQuotes and fix encoding bug (#35)

This commit is contained in:
FourierTransformer 2023-02-20 11:00:39 -06:00 committed by GitHub
parent 5be2f78119
commit 6238c5a0e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 38 deletions

View File

@ -143,9 +143,9 @@ file:close()
local output = ftcsv.encode(everyUser, ",", {fieldsToKeep={"Name", "Phone", "City"}}) local output = ftcsv.encode(everyUser, ",", {fieldsToKeep={"Name", "Phone", "City"}})
``` ```
- `noQuotes` - `onlyRequiredQuotes`
if `noQuotes` is set to `true`, the output will not include quotes around fields. if `onlyRequiredQuotes` is set to `true`, the output will only include quotes around fields that are quotes, have newlines, or contain the delimter.
```lua ```lua
local output = ftcsv.encode(everyUser, ",", {noQuotes=true}) local output = ftcsv.encode(everyUser, ",", {noQuotes=true})

View File

@ -645,15 +645,18 @@ local function delimitField(field)
end end
end end
local function delimitAndQuoteField(field) local function generateDelimitAndQuoteField(delimiter)
field = tostring(field) local generatedFunction = function(field)
if field:find('"') then field = tostring(field)
return '"' .. field:gsub('"', '""') .. '"' if field:find('"') then
elseif field:find('[\n,]') then return '"' .. field:gsub('"', '""') .. '"'
return '"' .. field .. '"' elseif field:find('[\n' .. delimiter .. ']') then
else return '"' .. field .. '"'
return field else
return field
end
end end
return generatedFunction
end end
local function escapeHeadersForLuaGenerator(headers) local function escapeHeadersForLuaGenerator(headers)
@ -681,7 +684,7 @@ local function csvLineGenerator(inputTable, delimiter, headers, options)
delimiter .. [["' .. args.delimitField(args.t[i]["]]) .. delimiter .. [["' .. args.delimitField(args.t[i]["]]) ..
[["]) .. '"\r\n']] [["]) .. '"\r\n']]
if options and options.noQuotes == true then if options and options.onlyRequiredQuotes == true then
outputFunc = [[ outputFunc = [[
local args, i = ... local args, i = ...
i = i + 1; i = i + 1;
@ -696,8 +699,8 @@ local function csvLineGenerator(inputTable, delimiter, headers, options)
arguments.t = inputTable arguments.t = inputTable
-- we want to use the same delimitField throughout, -- we want to use the same delimitField throughout,
-- so we're just going to pass it in -- so we're just going to pass it in
if options and options.noQuotes == true then if options and options.onlyRequiredQuotes == true then
arguments.delimitField = delimitAndQuoteField arguments.delimitField = generateDelimitAndQuoteField(delimiter)
else else
arguments.delimitField = delimitField arguments.delimitField = delimitField
end end
@ -716,7 +719,7 @@ end
local function initializeOutputWithEscapedHeaders(escapedHeaders, delimiter, options) local function initializeOutputWithEscapedHeaders(escapedHeaders, delimiter, options)
local output = {} local output = {}
if options and options.noQuotes == true then if options and options.onlyRequiredQuotes == true then
output[1] = table.concat(escapedHeaders, delimiter) .. '\r\n' output[1] = table.concat(escapedHeaders, delimiter) .. '\r\n'
else else
output[1] = '"' .. table.concat(escapedHeaders, '"' .. delimiter .. '"') .. '"\r\n' output[1] = '"' .. table.concat(escapedHeaders, '"' .. delimiter .. '"') .. '"\r\n'
@ -724,11 +727,11 @@ local function initializeOutputWithEscapedHeaders(escapedHeaders, delimiter, opt
return output return output
end end
local function escapeHeadersForOutput(headers, options) local function escapeHeadersForOutput(headers, delimiter, options)
local escapedHeaders = {} local escapedHeaders = {}
local delimitField = delimitField local delimitField = delimitField
if options and options.noQuotes == true then if options and options.onlyRequiredQuotes == true then
delimitField = delimitAndQuoteField delimitField = generateDelimitAndQuoteField(delimiter)
end end
for i = 1, #headers do for i = 1, #headers do
escapedHeaders[i] = delimitField(headers[i]) escapedHeaders[i] = delimitField(headers[i])
@ -771,7 +774,7 @@ local function initializeGenerator(inputTable, delimiter, options)
end end
validateHeaders(headers, inputTable) validateHeaders(headers, inputTable)
local escapedHeaders = escapeHeadersForOutput(headers, options) local escapedHeaders = escapeHeadersForOutput(headers, delimiter, options)
local output = initializeOutputWithEscapedHeaders(escapedHeaders, delimiter, options) local output = initializeOutputWithEscapedHeaders(escapedHeaders, delimiter, options)
return output, headers return output, headers
end end

View File

@ -61,15 +61,15 @@ describe("csv features", function()
assert.are.same(expected, actual) assert.are.same(expected, actual)
end) end)
it("should handle escaped doublequotes", function() it("should handle escaped doublequotes", function()
local expected = {} local expected = {}
expected[1] = {} expected[1] = {}
expected[1].a = 'A"B""C' expected[1].a = 'A"B""C'
expected[1].b = 'A""B"C' expected[1].b = 'A""B"C'
expected[1].c = 'A"""B""C' expected[1].c = 'A"""B""C'
local actual = ftcsv.parse('a;b;c\n"A""B""""C";"A""""B""C";"A""""""B""""C"', ";", {loadFromString=true}) local actual = ftcsv.parse('a;b;c\n"A""B""""C";"A""""B""C";"A""""""B""""C"', ";", {loadFromString=true})
assert.are.same(expected, actual) assert.are.same(expected, actual)
end) end)
it("should handle renaming a field", function() it("should handle renaming a field", function()
local expected = {} local expected = {}
@ -435,25 +435,41 @@ describe("csv features", function()
it("should handle encoding files (str test)", function() it("should handle encoding files (str test)", function()
local expected = '"a","b","c","d"\r\n"1","","foo","""quoted"""\r\n' local expected = '"a","b","c","d"\r\n"1","","foo","""quoted"""\r\n'
output = ftcsv.encode({ output = ftcsv.encode({
{ a = 1, b = '', c = 'foo', d = '"quoted"' }; { a = 1, b = '', c = 'foo', d = '"quoted"' };
}, ',') }, ',')
assert.are.same(expected, output) assert.are.same(expected, output)
end)
it("should handle encoding files (str test) with other delimiter", function()
local expected = '"a">"b">"c">"d"\r\n"1">"">"foo">"""quoted"""\r\n'
output = ftcsv.encode({
{ a = 1, b = '', c = 'foo', d = '"quoted"' };
}, '>')
assert.are.same(expected, output)
end) end)
it("should handle encoding files without quotes (str test)", function() it("should handle encoding files without quotes (str test)", function()
local expected = 'a,b,c,d\r\n1,,foo,"""quoted"""\r\n' local expected = 'a,b,c,d\r\n1,,"fo,o","""quoted"""\r\n'
output = ftcsv.encode({ output = ftcsv.encode({
{ a = 1, b = '', c = 'foo', d = '"quoted"' }; { a = 1, b = '', c = 'fo,o', d = '"quoted"' };
}, ',', {noQuotes=true}) }, ',', {onlyRequiredQuotes=true})
assert.are.same(expected, output) assert.are.same(expected, output)
end)
it("should handle encoding files without quotes with other delimiter (str test)", function()
local expected = 'a>b>c>d\r\n1>>fo,o>"""quoted"""\r\n'
output = ftcsv.encode({
{ a = 1, b = '', c = 'fo,o', d = '"quoted"' };
}, '>', {onlyRequiredQuotes=true})
assert.are.same(expected, output)
end) end)
it("should handle encoding files without quotes with certain fields to keep (str test)", function() it("should handle encoding files without quotes with certain fields to keep (str test)", function()
local expected = "b,c\r\n,foo\r\n" local expected = "b,c\r\n,foo\r\n"
output = ftcsv.encode({ output = ftcsv.encode({
{ a = 1, b = '', c = 'foo', d = '"quoted"' }; { a = 1, b = '', c = 'foo', d = '"quoted"' };
}, ',', {noQuotes=true, fieldsToKeep={"b", "c"}}) }, ',', {onlyRequiredQuotes=true, fieldsToKeep={"b", "c"}})
assert.are.same(expected, output) assert.are.same(expected, output)
end) end)
it("should handle headers attempting to escape", function() it("should handle headers attempting to escape", function()

View File

@ -94,7 +94,7 @@ describe("csv encode without quotes", function()
local jsonFile = loadFile("spec/json/" .. value .. ".json") local jsonFile = loadFile("spec/json/" .. value .. ".json")
local jsonDecode = cjson.decode(jsonFile) local jsonDecode = cjson.decode(jsonFile)
-- local parse = staecsv:ftcsv(contents, ",") -- local parse = staecsv:ftcsv(contents, ",")
local reEncodedNoQuotes = ftcsv.parse(ftcsv.encode(jsonDecode, ",", {noQuotes=true}), ",", {loadFromString=true}) local reEncodedNoQuotes = ftcsv.parse(ftcsv.encode(jsonDecode, ",", {onlyRequiredQuotes=true}), ",", {loadFromString=true})
-- local f = csv.openstring(contents, {separator=",", header=true}) -- local f = csv.openstring(contents, {separator=",", header=true})
-- local parse = {} -- local parse = {}
-- for fields in f:lines() do -- for fields in f:lines() do