local input = arg[1] local output = arg[2] local f = assert(io.open(input, "r")) local s = f:read("*a") f:close() local delim = "," local out, row = {}, {} local i, n = 1, #s local function csv_escape(cell) local needs_quotes = cell:find(delim, 1, true) or cell:find('"', 1, true) or cell:find("\\ ", 1, true) or cell:find("[\r\n]", 1, true) if needs_quotes then cell = cell:gsub('"','""') return '"' .. cell .. '"' end return cell end local function push_cell(cell) row[#row+1] = cell end local function end_row() out[#out+1] = row; row = {} end while i <= n do local c = s:sub(i,i) if c == '"' then i = i + 1 local buf = {} while i <= n do c = s:sub(i,i) if c == '"' then local nextc = s:sub(i+1,i+1) if nextc == '"' then buf[#buf+1] = '"' i = i + 2 else i = i + 1 break end else buf[#buf+1] = c i = i + 1 end end local cell = table.concat(buf) cell = cell:gsub("\r\n","\\\\{}") cell = cell:gsub("\n","\\\\{}") cell = cell:gsub("\r","\\\\{}") push_cell(cell) local c2 = s:sub(i,i) if c2 == delim then i = i + 1 elseif c2 == "\n" then i = i + 1; end_row() elseif c2 == "\r" then if s:sub(i+1,i+1) == "\n" then i = i + 2 else i = i + 1 end end_row() end else local buf = {} while i <= n do c = s:sub(i,i) if c == delim or c == "\n" or c == "\r" then break end buf[#buf+1] = c i = i + 1 end local cell = table.concat(buf) cell = cell:gsub("\r\n","\\\\{}") cell = cell:gsub("\n","\\\\{}") cell = cell:gsub("\r","\\\\{}") push_cell(cell) if i <= n and s:sub(i,i) == delim then i = i + 1 elseif i <= n and s:sub(i,i) == "\n" then i = i + 1; end_row() elseif i <= n and s:sub(i,i) == "\r" then if s:sub(i+1,i+1) == "\n" then i = i + 2 else i = i + 1 end end_row() end end end if #row > 0 then end_row() end local fo = assert(io.open(output, "w")) for r = 1, #out do local cells = out[r] for k = 1, #cells do if k > 1 then fo:write(delim) end fo:write(csv_escape(cells[k] or "")) end if r < #out then fo:write("\n") end end fo:close()