第2回エンペディア大賞」が2月いっぱい開催中です。2024年に作成された記事の中から、お気に入りの記事に投票しましょう!

モジュール:CSV

出典: 謎の百科事典もどき『エンペディア(Enpedia)』
ナビゲーションに移動 検索に移動

この説明文は 『 モジュール:CSV/doc 』 から呼び出されています。

require('CSV:Module').toArr2D({'A,B\nC,D'}) --> {{'A', 'B'}, {'C', 'D'}}
require('CSV:Module').toArr2D({'A,"B""\nC"\nD,E'}) --> {{'A', 'B"\nC'}, {'D', 'E'}}
local p = {}

-- ダブルクォートの囲み有り、ダブルクォートエスケープ有り
--(RFC4180に部分的に準拠)
function p.toArr2D(args)
	local csv = args[1]
	if csv == nil then return {} end
	local maxStep = args.maxStep or 1000 -- 最大セル解釈数
	
	local tbl = {}
	local row = {}
	
	-- パフォーマンス最適化のため変数宣言をfor文の外に
	local cellTxt = ''
	local start = 0 -- セル内容の開始idx
	local stop = 0 -- セル内容の終了idx
	local isLf = false
	
	local cursor = 1
	for i = 1, maxStep do -- maxStepステップ以上のループ拒否
		cellTxt = ''
		isLf = false
		
		-- ダブルクォート ~ ダブルクォート(改行|カンマ)  にマッチ
		start, stop = csv:find('^".-"[\n,]', cursor)
		if start ~= nil then
			cursor = stop + 1
			isLf =  csv:sub(stop, stop) == '\n'
			start = start + 1
			stop = stop - 2
		else
			-- ダブルクォート ~ ダブルクォート終端  にマッチ
			start, stop = csv:find('^".-"$', cursor)
			if start ~= nil then
				cursor = stop + 1
				isLf = true
				start = start + 1
				stop = stop - 1
			else
				-- ~ (改行|カンマ)  にマッチ
				start, stop = csv:find('.-[\n,]', cursor)
				if start ~= nil then
					cursor = stop + 1
					isLf =  csv:sub(stop, stop) == '\n'
					stop = stop - 1
				else -- 何にもマッチしない
					start = cursor
					cursor = #csv + 1
					isLf = true
					stop = cursor - 1
				end
			end
		end
		
		-- セル内容を取得
		cellTxt = csv:sub(start, stop):gsub('""', '"')
		table.insert(row, cellTxt)
		if isLf then
			table.insert(tbl, row)
			row = {}
		end
		
		if #csv < cursor then break end
	end
	return tbl
end

-- p.toTbl関数の動作確認用
-- WikiText上で {{#invoke:CSV|toTblPrint|csv=a,b,c}} のように呼び出せる
function p.toArr2DPrint(frame)
	local args = require('Module:Arguments').getArgs(frame)
	local csv = args[1] or args.csv
	if csv == nil then return '<<csv arg is nil!>>' end
	local tbl = p.toArr2D({csv})
	
	body = '<pre>'
	for rowIdx, row in pairs(tbl) do
		for colIdx, col in pairs(row) do
			body = body..'<<'..col..'>>'
		end
		body = body..'((改行))\n'
	end
	
	return frame:preprocess( body..'  len: '..#tbl..'</pre>' )
end

-- ダブルクォートの囲み無し、ダブルクォートエスケープ無し
function p.csv2tbl(args)
  local csv = args[1]
  if csv == nil then return {} end
  local tbl = {}
  local row = {}
  local rowIdx = 1
  for line, lf in csv:gmatch '([^\n]+)(\n*)' do
    row.lf = lf
    local colIdx = 1
    local isLast = false
    for cell, comma in line:gmatch '([^,]*)(,?)' do
      -- セルの情報をrowに溜める
      if not isLast then row[colIdx] = cell end
      isLast = #comma == 0
      colIdx = colIdx + 1
      if colIdx > 1000 then break end -- 1000列より大きい処理を拒否
    end
    -- 1行分の情報をtblの末尾に追加
    if 0 < #row then table.insert(tbl, row) end
    row = {}
    rowIdx = rowIdx + 1
    if rowIdx > 1000 then break end -- 1000行より大きい処理を拒否
  end
  return tbl
end

return p