Skip to content

Commit

Permalink
checking in files
Browse files Browse the repository at this point in the history
  • Loading branch information
FourierTransformer committed Feb 3, 2023
1 parent c858f99 commit 9dba7f8
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 10 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ file:close()
local output = ftcsv.encode(everyUser, ",", {fieldsToKeep={"Name", "Phone", "City"}})
```

- `noQuotes`

if `noQuotes` is set to `true`, the output will not include quotes around fields.

```lua
local output = ftcsv.encode(everyUser, ",", {noQuotes=true})
```


## Error Handling
Expand Down
55 changes: 45 additions & 10 deletions ftcsv.lua
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
local ftcsv = {
_VERSION = 'ftcsv 1.2.0',
_VERSION = 'ftcsv 1.3.0',
_DESCRIPTION = 'CSV library for Lua',
_URL = 'https://github.com/FourierTransformer/ftcsv',
_LICENSE = [[
The MIT License (MIT)
Copyright (c) 2016-2020 Shakil Thakur
Copyright (c) 2016-2023 Shakil Thakur
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -645,6 +645,17 @@ local function delimitField(field)
end
end

local function delimitAndQuoteField(field)
field = tostring(field)
if field:find('"') then
return '"' .. field:gsub('"', '""') .. '"'
elseif field:find('[\n,]') then
return '"' .. field .. '"'
else
return field
end
end

local function escapeHeadersForLuaGenerator(headers)
local escapedHeaders = {}
for i = 1, #headers do
Expand All @@ -658,7 +669,7 @@ local function escapeHeadersForLuaGenerator(headers)
end

-- a function that compiles some lua code to quickly print out the csv
local function csvLineGenerator(inputTable, delimiter, headers)
local function csvLineGenerator(inputTable, delimiter, headers, options)
local escapedHeaders = escapeHeadersForLuaGenerator(headers)

local outputFunc = [[
Expand All @@ -670,11 +681,26 @@ local function csvLineGenerator(inputTable, delimiter, headers)
delimiter .. [["' .. args.delimitField(args.t[i]["]]) ..
[["]) .. '"\r\n']]

if options and options.noQuotes == true then
outputFunc = [[
local args, i = ...
i = i + 1;
if i > ]] .. #inputTable .. [[ then return nil end;
return i, args.delimitField(args.t[i]["]] ..
table.concat(escapedHeaders, [["]) .. ']] ..
delimiter .. [[' .. args.delimitField(args.t[i]["]]) ..
[["]) .. '\r\n']]
end

local arguments = {}
arguments.t = inputTable
-- we want to use the same delimitField throughout,
-- so we're just going to pass it in
arguments.delimitField = delimitField
if options and options.noQuotes == true then
arguments.delimitField = delimitAndQuoteField
else
arguments.delimitField = delimitField
end

return luaCompatibility.load(outputFunc), arguments, 0

Expand All @@ -688,17 +714,26 @@ local function validateHeaders(headers, inputTable)
end
end

local function initializeOutputWithEscapedHeaders(escapedHeaders, delimiter)
local function initializeOutputWithEscapedHeaders(escapedHeaders, delimiter, options)
local output = {}
output[1] = '"' .. table.concat(escapedHeaders, '"' .. delimiter .. '"') .. '"\r\n'
if options and options.noQuotes == true then
output[1] = table.concat(escapedHeaders, delimiter) .. '\r\n'
else
output[1] = '"' .. table.concat(escapedHeaders, '"' .. delimiter .. '"') .. '"\r\n'
end
return output
end

local function escapeHeadersForOutput(headers)
local function escapeHeadersForOutput(headers, options)
local escapedHeaders = {}
local delimitField = delimitField
if options and options.noQuotes == true then
delimitField = delimitAndQuoteField
end
for i = 1, #headers do
escapedHeaders[i] = delimitField(headers[i])
end

return escapedHeaders
end

Expand Down Expand Up @@ -736,16 +771,16 @@ local function initializeGenerator(inputTable, delimiter, options)
end
validateHeaders(headers, inputTable)

local escapedHeaders = escapeHeadersForOutput(headers)
local output = initializeOutputWithEscapedHeaders(escapedHeaders, delimiter)
local escapedHeaders = escapeHeadersForOutput(headers, options)
local output = initializeOutputWithEscapedHeaders(escapedHeaders, delimiter, options)
return output, headers
end

-- works really quickly with luajit-2.1, because table.concat life
function ftcsv.encode(inputTable, delimiter, options)
local output, headers = initializeGenerator(inputTable, delimiter, options)

for i, line in csvLineGenerator(inputTable, delimiter, headers) do
for i, line in csvLineGenerator(inputTable, delimiter, headers, options) do
output[i+1] = line
end

Expand Down
24 changes: 24 additions & 0 deletions spec/feature_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,30 @@ describe("csv features", function()
assert.are.same(expected, actual)
end)

it("should handle encoding files (str test)", 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)

it("should handle encoding files without quotes (str test)", function()
local expected = 'a,b,c,d\r\n1,,foo,"""quoted"""\r\n'
output = ftcsv.encode({
{ a = 1, b = '', c = 'foo', d = '"quoted"' };
}, ',', {noQuotes=true})
assert.are.same(expected, output)
end)

it("should handle encoding files without quotes with certain fields to keep (str test)", function()
local expected = "b,c\r\n,foo\r\n"
output = ftcsv.encode({
{ a = 1, b = '', c = 'foo', d = '"quoted"' };
}, ',', {noQuotes=true, fieldsToKeep={"b", "c"}})
assert.are.same(expected, output)
end)

it("should handle headers attempting to escape", function()
local expected = {}
expected[1] = {}
Expand Down
17 changes: 17 additions & 0 deletions spec/parse_encode_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,20 @@ describe("csv encode", function()
end)
end
end)

describe("csv encode without quotes", function()
for _, value in ipairs(files) do
it("should handle " .. value, function()
local jsonFile = loadFile("spec/json/" .. value .. ".json")
local jsonDecode = cjson.decode(jsonFile)
-- local parse = staecsv:ftcsv(contents, ",")
local reEncodedNoQuotes = ftcsv.parse(ftcsv.encode(jsonDecode, ",", {noQuotes=true}), ",", {loadFromString=true})
-- local f = csv.openstring(contents, {separator=",", header=true})
-- local parse = {}
-- for fields in f:lines() do
-- parse[#parse+1] = fields
-- end
assert.are.same(jsonDecode, reEncodedNoQuotes)
end)
end
end)

0 comments on commit 9dba7f8

Please sign in to comment.