模块:Csv.lua:修订间差异

来自「荏苒之境」
页面内容被替换为“local function parse_row(input, sep, pos) local row = {} local pos = pos or 1 while true do local c = string.sub(input, pos, pos) if c == "" then break end if c == '"' then local text = '' local s, e, txt, c1 repeat s, e, txt, c1 = string.find(input, '^(.-")(.)', pos + 1) text = text..txt pos = e until c1 == sep or c1 == "\r" or c1 == "\n" table.insert(row, string.sub(text, 1, -2)) c = c1 pos = pos + 1 else local s…”
标签替换
无编辑摘要
 
(未显示同一用户的11个中间版本)
第1行: 第1行:
--[[
Modifed by Phlamcenth Sicusa from:
    CSV Library v1 (Author: Michael Lutz, 2022-12-14)
    Built on: http://lua-users.org/wiki/LuaCsv
   
    csv.load = function(filename, delimiter, header)
    filename := CSV file to load
    delimiter := Delimiter (";", ",", "\t", etc..), default = ','
    header := (optional) if first line is a header (true/false), default = false
   
    automatically removes quotes from text
    returns a table
   
    csv.save = function(filename, delimiter, data, header)
    filename := CSV file to write to
    delimiter := Delimiter (";", ",", "\t", etc..), default = ','
    data := a Lua table that holds the rows and columns
    header := a Lua table that holds the names of the columns e.g. { "Name", "Address", "Email", ... }
--]]
local unpack = unpack or table.unpack
local BYTE_QUOTE = string.byte('"')
local BYTE_ENTER = string.byte('\r')
local BYTE_NEWLINE = string.byte('\n')
local BYTE_COMMA = string.byte(',')
local function parse_quoted(input, sep, pos, s)
    while true do
        local c = string.byte(input, pos)
        if c == BYTE_QUOTE then
            if string.byte(input, pos + 1) == BYTE_QUOTE then
                pos = pos + 1
                s[#s+1] = '"'
            else
                return pos + 1
            end
        elseif c == sep then
            return pos + 1
        elseif c == nil then
            return pos
        else
            s[#s+1] = c
        end
        pos = pos + 1
    end
end
local function bytes_to_string(bytes)
    if #bytes == 0 then
        return ""
    else
        return string.char(unpack(bytes))
    end
end
local function parse_row(input, sep, pos)
local function parse_row(input, sep, pos)
local row = {}
    local r = {}
local pos = pos or 1
    local s = {}
while true do
    while true do
local c = string.sub(input, pos, pos)
        local c = string.byte(input, pos)
if c == "" then break end
        if c == sep then
if c == '"' then
            r[#r+1] = bytes_to_string(s)
local text = ''
            s = {}
local s, e, txt, c1
            pos = pos + 1
repeat
        elseif c == nil then
s, e, txt, c1 = string.find(input, '^(.-")(.)', pos + 1)
            if #r ~= 0 or #s ~= 0 then
text = text..txt
                r[#r+1] = bytes_to_string(s)
pos = e
            end
until c1 == sep or c1 == "\r" or c1 == "\n"
            break
table.insert(row, string.sub(text, 1, -2))
        elseif c == BYTE_NEWLINE then
c = c1
            r[#r+1] = bytes_to_string(s)
pos = pos + 1
            pos = pos + 1
else
            break
local s, e, text, c1 = string.find(
        elseif c == BYTE_ENTER then
input, "^([^%"..sep.."\r\n]-)([%"..sep.."\r\n])", pos)
            pos = pos + 1
pos = e+1
        elseif c == BYTE_QUOTE then
table.insert(row, text)
            pos = parse_quoted(input, sep, pos + 1, s)
c = c1
        else
end
            s[#s+1] = c
if c == "\n" then return row, pos
            pos = pos + 1
elseif c == "\r" then return row, pos+1 end
        end
end
    end
    return r, pos
end
end


local csv = {}
local csv = {}


csv.parse = function(src, delimiter, header)
csv.parse_row = function(str, delimiter)
local sep = string.sub(delimiter,1,1) or ','
local sep = delimiter and string.byte(delimiter) or BYTE_COMMA
local pos = 1
return parse_row(str, sep, 1)
local t_csv = {}
end
local f_header = nil
 
local t_header = {}
csv.parse = function(str, enable_header, delimiter)
if header then
    local sep = delimiter and string.byte(delimiter) or BYTE_COMMA
t_header,pos = parse_row(src, sep, pos)
    local pos = 1
local head = {}
    local csv = {}
for i,v in ipairs(t_header) do
    local row_mt = nil
head[v] = i
 
end
    if enable_header then
f_header = function (t,k)
        local header
local i = head[k]
        header, pos = parse_row(str, sep, pos)
if i then
        csv.header = header
return t[i]
 
end
        local head_map = {}
return nil
        for i = 1, #header do
end
            head_map[header[i]] = i
end
        end
 
        row_mt = {
            __index = function (t,k)
                local i = head_map[k]
                if i then
                    return t[i]
                end
                return nil
            end
        }
    end
 
    local row
    row, pos = parse_row(str, sep, pos)
 
    while #row ~= 0 do
        if row_mt then
            setmetatable(row, row_mt)
        end
        csv[#csv+1] = row
        row, pos = parse_row(str, sep, pos)
    end


local row = {}
    return csv
row, pos = parse_row(src, sep, pos)
while row do
if header then
setmetatable(row, { __index = f_header })
end
table.insert(t_csv, row)
row, pos = parse_row(csv, sep, pos)
end
return t_csv, t_header
end
end


local function format_csv(str, sep)
local function format_csv(str, sep)
local str, matches = string.gsub(str or "", '"', '""')
    local str, matches = string.gsub(str or "", '"', '""')
if (string.find(str, "[%"..sep.."\r\n]") or (matches > 0)) then
    if string.find(str, "[%"..sep.."\r\n]") or matches > 0 then
return '"'..str..'"'
        return '"'..str..'"'
end
    end
return str
    return str
end
end


csv.format = function(delimiter, data, header)
csv.format_row = function(row, delimiter)
local r = {}
local r = {}
local sep = string.sub(delimiter,1,1) or ','
local sep = delimiter and string.sub(delimiter,1,1) or ','
if header then
for i,v in ipairs(header) do
for i = 1, #row do
r[#r+1] = format_csv(v, sep)
        r[#r+1] = format_csv(row[i], sep)
r[#r+1] = sep
        r[#r+1] = sep
end
r[#r] = "\n"
end
end
r[#r] = nil
return table.concat(r)
end
csv.format = function(data, header, delimiter)
    local r = {}
    local sep = delimiter and string.sub(delimiter,1,1) or ','
    if header then
        for i = 1, #header do
            r[#r+1] = format_csv(header[i], sep)
            r[#r+1] = sep
        end
        r[#r] = "\n"
    end


for i,v in ipairs(data) do
    for i = 1, #data do
for i2,v2 in ipairs(v) do
        local v = data[i]
r[#r+1] = format_csv(v2, sep)
        for j = 1, #v do
r[#r+1] = sep
            r[#r+1] = format_csv(v[j], sep)
end
            r[#r+1] = sep
r[#r] = "\n"
        end
end
        r[#r] = "\n"
return table.concat(r)
    end
   
r[#r] = nil
    return table.concat(r)
end
end


return csv
return csv

2025年8月4日 (一) 17:30的最新版本

此模块的文档可以在模块:Csv.lua/doc创建

--[[
Modifed by Phlamcenth Sicusa from:
    CSV Library v1 (Author: Michael Lutz, 2022-12-14)
    Built on: http://lua-users.org/wiki/LuaCsv
    
    csv.load = function(filename, delimiter, header)
    filename := CSV file to load
    delimiter := Delimiter (";", ",", "\t", etc..), default = ','
    header := (optional) if first line is a header (true/false), default = false
    
    automatically removes quotes from text
    returns a table
    
    csv.save = function(filename, delimiter, data, header)
    filename := CSV file to write to
    delimiter := Delimiter (";", ",", "\t", etc..), default = ','
    data := a Lua table that holds the rows and columns
    header := a Lua table that holds the names of the columns e.g. { "Name", "Address", "Email", ... }
--]]

local unpack = unpack or table.unpack

local BYTE_QUOTE = string.byte('"')
local BYTE_ENTER = string.byte('\r')
local BYTE_NEWLINE = string.byte('\n')
local BYTE_COMMA = string.byte(',')

local function parse_quoted(input, sep, pos, s)
    while true do
        local c = string.byte(input, pos)
        if c == BYTE_QUOTE then
            if string.byte(input, pos + 1) == BYTE_QUOTE then
                pos = pos + 1
                s[#s+1] = '"'
            else
                return pos + 1
            end
        elseif c == sep then
            return pos + 1
        elseif c == nil then
            return pos
        else
            s[#s+1] = c
        end
        pos = pos + 1
    end
end

local function bytes_to_string(bytes)
    if #bytes == 0 then
        return ""
    else
        return string.char(unpack(bytes))
    end
end

local function parse_row(input, sep, pos)
    local r = {}
    local s = {}
    while true do
        local c = string.byte(input, pos)
        if c == sep then
            r[#r+1] = bytes_to_string(s)
            s = {}
            pos = pos + 1
        elseif c == nil then
            if #r ~= 0 or #s ~= 0 then
                r[#r+1] = bytes_to_string(s)
            end
            break
        elseif c == BYTE_NEWLINE then
            r[#r+1] = bytes_to_string(s)
            pos = pos + 1
            break
        elseif c == BYTE_ENTER then
            pos = pos + 1
        elseif c == BYTE_QUOTE then
            pos = parse_quoted(input, sep, pos + 1, s)
        else
            s[#s+1] = c
            pos = pos + 1
        end
    end
    return r, pos
end

local csv = {}

csv.parse_row = function(str, delimiter)
	local sep = delimiter and string.byte(delimiter) or BYTE_COMMA
	return parse_row(str, sep, 1)
end

csv.parse = function(str, enable_header, delimiter)
    local sep = delimiter and string.byte(delimiter) or BYTE_COMMA
    local pos = 1
    local csv = {}
    local row_mt = nil

    if enable_header then
        local header
        header, pos = parse_row(str, sep, pos)
        csv.header = header

        local head_map = {}
        for i = 1, #header do
            head_map[header[i]] = i
        end

        row_mt = {
            __index = function (t,k)
                local i = head_map[k]
                if i then
                    return t[i]
                end
                return nil
            end
        }
    end

    local row
    row, pos = parse_row(str, sep, pos)

    while #row ~= 0 do
        if row_mt then
            setmetatable(row, row_mt)
        end
        csv[#csv+1] = row
        row, pos = parse_row(str, sep, pos)
    end

    return csv
end

local function format_csv(str, sep)
    local str, matches = string.gsub(str or "", '"', '""')
    if string.find(str, "[%"..sep.."\r\n]") or matches > 0 then
        return '"'..str..'"'
    end
    return str
end

csv.format_row = function(row, delimiter)
	local r = {}
	local sep = delimiter and string.sub(delimiter,1,1) or ','
	
	for i = 1, #row do
        r[#r+1] = format_csv(row[i], sep)
        r[#r+1] = sep
	end
	
	r[#r] = nil
	return table.concat(r)
end

csv.format = function(data, header, delimiter)
    local r = {}
    local sep = delimiter and string.sub(delimiter,1,1) or ','

    if header then
        for i = 1, #header do
            r[#r+1] = format_csv(header[i], sep)
            r[#r+1] = sep
        end
        r[#r] = "\n"
    end

    for i = 1, #data do
        local v = data[i]
        for j = 1, #v do
            r[#r+1] = format_csv(v[j], sep)
            r[#r+1] = sep
        end
        r[#r] = "\n"
    end
    
	r[#r] = nil
    return table.concat(r)
end

return csv