适配含空值CSV的Lua匹配模式及数组转换方案问询
解决Lua解析含空值CSV的问题
你的原代码使用[^,]+作为匹配模式,这个模式只会匹配非逗号的连续字符,遇到连续逗号(对应空值)时会直接跳过,导致解析出的列数缺失——比如示例中Refund,,10.00会被解析成Refund和10.00两个字段,漏掉中间的空值列。
下面提供两种可行的解决思路:
思路1:改进正则匹配模式
通过调整匹配模式,让它能捕获空值(包括连续逗号之间的空字符串、行首/行尾的空字段)。可以使用(.-)(,|$)的模式,同时在每行末尾额外添加一个逗号,统一处理最后一个字段的边界:
local csvfilename = "/mnt/nas/Fireflyiii.csv" local function create_array_from_file(csvfilename) local file = assert(io.open(csvfilename, "r")) local arr = {} for line in file:lines() do local row = {} -- 末尾加逗号,确保最后一个字段也能被捕获 for field in string.gmatch(line .. ",", "(.-),") do table.insert(row, field) end table.insert(arr, row) end return arr end
这种方法简洁高效,但缺点是无法处理字段内包含逗号的场景(比如带引号包裹的"Doe, John"),如果你的CSV没有这类复杂字段,这个方案足够用。
思路2:手动分割字符串(更灵活)
通过遍历字符串中逗号的位置,手动提取每个字段,这种方式可以灵活处理空值,还能扩展支持带引号的复杂字段:
local csvfilename = "/mnt/nas/Fireflyiii.csv" local function split_csv_line(line) local row = {} local start = 1 local len = #line while start <= len do local comma_pos = string.find(line, ",", start) if not comma_pos then -- 处理最后一个字段 table.insert(row, string.sub(line, start)) break end -- 提取当前字段(包括空值,比如连续逗号时start == comma_pos,sub结果为空字符串) table.insert(row, string.sub(line, start, comma_pos - 1)) start = comma_pos + 1 end -- 如果行尾是逗号,补充最后一个空字段 if string.sub(line, -1) == "," then table.insert(row, "") end return row end local function create_array_from_file(csvfilename) local file = assert(io.open(csvfilename, "r")) local arr = {} for line in file:lines() do local row = split_csv_line(line) table.insert(arr, row) end return arr end
如果你的CSV存在带引号且包含逗号的字段,可以进一步扩展split_csv_line函数,判断字段是否以引号开头,然后找到对应的结束引号再分割:
local function split_csv_line(line) local row = {} local start = 1 local len = #line while start <= len do local char = string.sub(line, start, start) -- 处理带双引号包裹的字段(可修改为单引号适配你的场景) if char == '"' then local end_quote = string.find(line, '"', start + 1) if not end_quote then -- 未找到结束引号,将剩余内容作为字段 table.insert(row, string.sub(line, start)) break end -- 提取引号内的内容 table.insert(row, string.sub(line, start + 1, end_quote - 1)) -- 跳过结束引号和后面的逗号 start = end_quote + 1 if start <= len and string.sub(line, start, start) == "," then start = start + 1 end else local comma_pos = string.find(line, ",", start) if not comma_pos then table.insert(row, string.sub(line, start)) break end table.insert(row, string.sub(line, start, comma_pos - 1)) start = comma_pos + 1 end end if string.sub(line, -1) == "," then table.insert(row, "") end return row end
这个方案能覆盖绝大多数CSV场景,包括空值、带逗号的字段等。
内容的提问来源于stack exchange,提问作者nodecentral
相关产品推荐
相关产品推荐

