lua(實現代碼(1)):使用lua將excel中的配置數據生成sql文件

本程序主要用於遊戲編程等相關項目中,產品人員使用excel設計數據之後直接將相關數據生成sql,可以導入數據庫

excel格式及相關的使用方法,請下載數據包http://download.csdn.net/download/wentianyao/9401274

該庫使用了luacom組件所以只能在windows使用


-- excel文件解析成sql文件
-- ws
-- 2015.5

------------------------------------------------------------------------
--配置部分
-- 生成sql目標文件的前綴
local FilePrefix = "conf_"
-- 識別excel中表的前綴
local TablePrefix = "conf_"

------------------------------------------------------------------------
-- 加載庫文件
package.path=package.path..";..\\?.lua;..\\..\\scripts\\?.lua"
require("luacom")
require("json")
local lfs = require"lfs"    -- 加載文件處理庫

------------------------------------------------------------------------
local exl2sql={}

-- 設置類型長度 語句配置
--                    數據類型        創建表的時候用(屬性創建語句)               數據爲空的時候的替換數據
exl2sql.STRINGTYPE ={["UINT11"] =     {"int(11) unsigned NOT NULL DEFAULT '0'", 0},
           ["INT11"] =     {"int(11) NOT NULL DEFAULT '0'", 0},
           ["INT32"] =     {"int(32) NOT NULL DEFAULT '0'", 0},
           ["INT64"] =     {"int(64) NOT NULL DEFAULT '0'", 0},
           ["STRING32"] =    {"varchar(32) DEFAULT NULL", "\'\'"},   
           ["STRING64"] =    {"varchar(64) DEFAULT NULL", "\'\'"},
           ["STRING128"] =   {"varchar(128) DEFAULT NULL", "\'\'"},
           ["STRING256"] =   {"varchar(256) DEFAULT NULL", "\'\'"},
           ["STRING512"] =   {"varchar(512) DEFAULT NULL", "\\'\''\'"},
           ["STRING1024"] =   {"varchar(1024) DEFAULT NULL", "\'\'"},
           ["STRING2048"] =   {"varchar(2048) DEFAULT NULL", "\'\'"},
           ["DATETIME"] =   {"datetime default '0000-00-00 00:00:00'", "\'\'"},
           ["JSON256"] =    {"varchar(256)  DEFAULT NULL", "\'\'"},
           ["JSON1024"] =    {"varchar(1024) DEFAULT NULL", "\'\'"},
           ["JSON2048"] =    {"varchar(2048) DEFAULT NULL", "\'\'"}}


-- 字符串的處理“^%s”表示匹配所有非s裏面的數據,*表示匹配前一個字符串0次或多次,(.-)表示匹配.或者-,%s*表示0次或多次匹配s
function exl2sql.trim(s)
  return s:match "^%s*(.-)%s*$"
end


-- 將字符串s中的符合tMode的子串替換爲tReplace
function exl2sql.escape(s)
    local tMode="[%z\'\"\\\26\b\n\r\t]"
    local tReplace={
        ['\0']='\\0',
        ['\'']='\\\'',
        ['\"']='\\\"',
        ['\\']='\\\\',
        ['\26']='\\z',
        ['\b']='\\b',
        ['\n']='\\n',
        ['\r']='\\r',
        ['\t']='\\t'
    }
    return string.gsub(s, tMode, tReplace)
end

 

-- 獲取目標目錄下的有所*.xlsx文件
-- 參數path:爲純路徑
function exl2sql.GetAllXlsxFile(path)
  local FileList = {}
  local NoSuffixFileList = {} -- 無後綴的文件列表
   -- 循環遍歷
   for ofile in lfs.dir(path) do
     if ofile ~= nil then
       -- 獲取文件屬性
      local f = path..'\\'..ofile
      --local f = ofile
      -- 拆分文件及後綴
      local ext = f:match(".+%.(%w+)$")
      -- 獲取本目錄下的所有的xlsx文件
      if ext == "xlsx" then
         -- 排除過度文件
         local filename = string.sub(ofile, 0, 2)
         if filename ~= "~$" then
         table.insert(FileList, f)
         local name = string.sub(f,0,f:match(".+()%.%w+$")).."sql"  -- 生成的文件名
         table.insert(NoSuffixFileList, name)    
        end
      end
     end  
  end
  return FileList, NoSuffixFileList
end

 

-- 獲取文檔中每個sheet信息
-- 參數:ExlObj表示創建的Excel對象,BookObj表示打開的excel對象
-- 返回值: 返回所有有效的sheet列表,否則返回nil
function exl2sql.GetSheetInfo(ExlObj, BookObj)
   -- 遍歷文件中的sheet標籤(目前暫定標籤格式爲TablePrefix指定),其他的不識別
   local SheetList = {} -- 目標文件所有的sheet信息
   
    -- 獲取sheet文件數量
    local temp = BookObj.Sheets.Count
    -- 遍歷所有的表名(excel中默認的起始編號是1)

    for i = 0,(temp - 1) do
       -- 獲取該table的數據特徵(列名,類型)
        local tSheet=BookObj.Sheets(i + 1) -- 獲取當前的sheet文件
       local tabname = ExlObj.ActiveWorkbook.Sheets(i+1).Name  -- 獲取sheet名
       -- 識別是不是要生成的TablePrefix指定的前綴表
       local TablePrefixLen = string.len(TablePrefix) -- 獲取指定前綴的長度
       local PreTabeName = string.sub(tabname, 0, TablePrefixLen) -- 截取sheet名字的前n個用於識別是不是前綴
       
      -- 如果是要符合規範的sheet文件
       if PreTabeName == TablePrefix then 
          -- 獲取sheet的屬性(編號及名字)
          local tabattr = {TableIndx = nil, TableName = nil, ColList = {}}  -- 表的基礎結構(表編號,sheet名字,表中所有有效列的信息)
          tabattr.TableIndx = i + 1  -- 當前表所在的sheet編號
          tabattr.TableName = tabname  -- sheet對應的將要生成的name
          tabattr.ColList = {}
          print("INFO_1: read sheet"..tabattr.TableIndx.." :name".. tabname.. " base info start...")

          -- 遍歷列,獲取列名(注:excel表中的必需第二行是列名)    
          local bBreak = true -- 跳出中斷
          local ColNum = 1 -- 列編號
          while bBreak == true do
             local SheetInfo = {} -- 表的列信息(列編號,列名說明,列名,列類型)
             SheetInfo.ColIndex = ColNum
             SheetInfo.ColNameText = tostring(tSheet.Cells(1, ColNum).Value2) -- 獲取列名備註
             SheetInfo.ColName = tostring(tSheet.Cells(2, ColNum).Value2) -- 獲取列名
             SheetInfo.ColType = tostring(tSheet.Cells(3, ColNum).Value2) -- 獲取目標列的數據類型
                  
             -- 索引結束
             if SheetInfo.ColName == "nil" then
                 print("INFO_2: the sheet: "..tabattr.TableName.." has check end...")
                bBreak = false  -- 循環中斷
                break
             end
             
             if SheetInfo.ColType == "nil" then --缺少列類型
                   print("ERROR_1: this sheet: "..tabattr.TableName.." NO."..SheetInfo.ColIndex.." column have not Type!")
                   return nil --end
              else
                    -- 類型檢查
                    if exl2sql.STRINGTYPE[SheetInfo.ColType] == nil then
                    print("ERROR_2: this sheet: "..tabattr.TableName.." NO."..SheetInfo.ColIndex.." column type is wrong!")
                    return nil
                    end
                   
                  print("INFO_3: NO."..ColNum.." column".." Name: "..SheetInfo.ColName.." Type:"..SheetInfo.ColType)
                  -- 將列信息添加到表信息中
                  table.insert(tabattr.ColList, SheetInfo)
                  ColNum = ColNum + 1  -- 列編號+1
             end
          end -- while bBreak do
          
          -- 提示信息
          if table.getn(tabattr.ColList) == 0 then
            print("ERROR_3: Have no any data from sheet"..ColNum.."!")
            else
            -- 表信息插入sheet列表
             table.insert(SheetList, tabattr)
          end
          
       end -- if PreTabeName == TablePrefix then
       
    end -- for i = 0,(temp - 1) do
   
    -- 檢查SheetList是否爲空
    if 0 >= table.getn(SheetList) then
      return nil
    end
   
    -- 返回sheet列表
    return SheetList
end

 

-- 根據列類型獲取數據
-- 參數:ColType表示列類型,Data表示列原始數據
function exl2sql.GetDataByType(ColType, Data)
    -- 若讀取的數據爲空
  if Data == nil then
    local nildata = exl2sql.STRINGTYPE[ColType][2] -- 獲取數據爲空的時候的替換數據
    return nildata
  end
  
  -- 若有數據
  local bData = nil
  if ColType == "UINT11" or ColType == "INT11" or ColType == "INT32" or ColType == "INT64"then
     bData = tonumber(Data)
    else
       bData = exl2sql.trim(tostring(Data))
  end
  
  
  -- 特殊處理
  -- 特殊處理1,json類型處理規則
  local TypeSuf = string.sub(ColType, 0, 4) -- 獲取類型前4個,識別是不是json
  if TypeSuf == "JSON" then
   bData = exl2sql.escape(json.encode(json.decode(bData)))
  end
  
  -- 對於字符串加"“”"處理
  if ColType == "UINT11" or ColType == "INT11" or ColType == "INT32" or ColType == "INT64"then
  else
       bData = "\""..bData.."\""
  end
  
  return bData
end

 


-- 執行sql的創建,包括表的創建和數據的插入
-- 參數:ExlObj表示創建的Excel對象,BookObj表示打開的excel對象,SheetObj表示已經檢查過的有效的sheet,WriteOutFile輸出的sql文件句柄
function exl2sql.CreateSql(ExlObj, BookObj, SheetObj, WriteOutFile) 
   -- 創建表的過程(獲取執行語句)
   local tCreateSql = ""
   local tNameList = {} -- 列名列表
   local tNameType = {} -- 列類型
   for i, ColInfo in ipairs(SheetObj.ColList) do
       -- 根據類型獲取語句
       local TypeString = exl2sql.STRINGTYPE[ColInfo.ColType][1] -- 獲取對應的類型長度
      tCreateSql = tCreateSql .. "`"..ColInfo.ColName.."` "..TypeString
      table.insert(tNameList, ColInfo.ColName)
      table.insert(tNameType, ColInfo.ColType)
   end
  
  
   -- 獲取數據同時進行數據檢查
   -- 提供主鍵檢查
   local tSheet=BookObj.Sheets(SheetObj.TableIndx) -- 獲取當前的sheet文件
   -- 遍歷所有數據(*目前從第4行開始)
   local bBreak = true
   local RowlIndex = 4 --遍歷的數據行(目前是從第4行開始)
   local ColNumMax = table.getn(tNameList) -- 獲取有多少數據列
   local RowKeyList = {} -- 所有主鍵值
   local RowDataList = {} -- 所有行數據
   while bBreak do -- 一直循環遍歷所有數據
       local RowData = {} -- 行數據
        -- 獲主鍵數據
        local keydata = tSheet.Cells(RowlIndex, 1).Value2
        if keydata == nil then  -- 認爲數據結束
           bBreak = false
           break  -- 中斷
         end
        
         -- 轉換成有效數據
         local colKeydata = exl2sql.GetDataByType(tNameType[1], keydata)
        -- 主鍵檢查
         if RowKeyList[colKeydata] ~= nil then -- 表示該主鍵已經被用過了
          -- 如果聯繫的兩個值都爲
          print("ERROR_4: in sheet"..SheetObj.TableName.." DOUBLE PRIMARY KEY value:"..colKeydata) return nil
         end  
         
       -- 插入主鍵
       RowKeyList[colKeydata] = 1 -- 表示該主鍵已經使用
       -- 插入第列數據
       table.insert(RowData, colKeydata)
      
       -- 其他數據
      for j = 2, ColNumMax do
          -- 獲取列數據
          local coldata = exl2sql.GetDataByType(tNameType[j], tSheet.Cells(RowlIndex, j).Value2)
         table.insert(RowData, coldata)
      end
      
      -- 加入數據列
      table.insert(RowDataList, RowData)
  
      RowlIndex = RowlIndex + 1 -- 行遞增
   end
  
  
   -- 插入註釋
   WriteOutFile:write("\n\n-- -----------------------------\n-- "..SheetObj.TableName.."\n-- -------------------------------")
  
   -- 插入表頭
   WriteOutFile:write("\nDROP TABLE IF EXISTS `"..SheetObj.TableName.."`; \nCREATE TABLE `"..SheetObj.TableName.."` (\n")
  
   -- 插入表尾(包括建立列,設置主鍵(默認第一列就是主鍵),引擎類型,編碼方式)
   WriteOutFile:write(tCreateSql.." PRIMARY KEY (`"..SheetObj.ColList[1].ColName.."`)\n) ENGINE=MyISAM DEFAULT CHARSET=utf8;\n\n")
  
   -- 循環插入數據的過程
   --WriteOutFile:write("insert into `"..SheetObj.TableName.."`(`"..table.concat(tNameList, "','").."`) values ("..table.concat (RowDataList, ", ")..";\n")
   for i, col in ipairs(RowDataList) do
   WriteOutFile:write("INSERT INTO `"..SheetObj.TableName.."` VALUES ("..table.concat(col, ", ")..");\n")
   end
  
 
   print("INFO_4: Table: ".. SheetObj.TableName.." create success ...")
  
   return 1
  
end

 


-- 打開excel表
-- 參數tExl:爲excel讀寫句柄;file:爲完整的文件路徑及文件名;filename爲生成的文件名
function exl2sql.OpenExcel(tExl, file, filename)
    -- 打印起始信息
    print("INFO:file: "..file.." to "..filename.." start...")

    if tExl == nil then print("ERROR_5: need lua library!") return end
    -- 加載目標文件
    local tBook=tExl.Workbooks:Open(file)
    if tBook == nil then print("ERROR_6: Excel file: "..file.." open fail!")  tExl.Application:quit() tExl=nil return end
   
    -- 獲取excel中所有有效的sheet信息
    local sheetlist = exl2sql.GetSheetInfo(tExl, tBook)
    if sheetlist == nil then tBook:close()  tExl.Application:quit() tExl=nil return end
      
    -- 打開目標sql文件
    os.remove(filename) -- 先刪除一遍
    local WriteOutFile = io.open(filename, "a")
    if WriteOutFile == nil then print("ERROR_7: Create sql file: "..filename.." fail!") tBook:close() tExl.Application:quit() tExl=nil return end 
    
    -- 插入文件編碼方式
   -- WriteOutFile:write("set names \"utf8\";\n")
      WriteOutFile:write("\nSET FOREIGN_KEY_CHECKS=0;\n")
  
    -- 向sql文件中寫入數據
    for i, v in ipairs(sheetlist)do
     local res = exl2sql.CreateSql(tExl, tBook, v, WriteOutFile)
     -- 如果數據寫入失敗,就刪除sql文件
     if res == nil then
       WriteOutFile:close()
      print("ERROR_8: Sheet write fail !")
      os.remove(filename)
      -- 關閉excel文件
        tBook:close()
        return
     end
    end
   
    -- 關閉sql文件
    WriteOutFile:close()
   
    -- 關閉excel文件
    tBook:close()
    tExl.Application:quit()
    tExl=nil

   
    -- 打印
    print("INFO:file: "..file.." to "..filename.." end...success!")
   
    return
end

 


--程序入口
function exl2sql.MainStart()
    -- 創建讀取文件類型
   local tExl=luacom.CreateObject("Excel.Application")
  
  -- 獲取當前目錄下的所有excel文件
  local AllFile, AllFileName = exl2sql.GetAllXlsxFile(lfs.currentdir());
  for i,ofile in ipairs(AllFile) do
    -- 獲取單個文件的所有table屬性
    local exlAllTable = exl2sql.OpenExcel(tExl, ofile, AllFileName[i])
    --if exlAllTable == nil then return end
    print("\n\n\n")
  end
 
   -- 關閉句柄
   tExl.Application:quit()
    tExl=nil
 
end

 


exl2sql.MainStart()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章