为何要把excel转csv,
CSV是纯文本文件,Excel是二进制文件,Excel包含很多格式信息在里面。因此CSV文件可以用记事本打开,而Excel用记事本打开会报错。
请注意:csv文件从本质上说是纯文本的文件,没有压缩,所以一般都比同内容的excel文件体积要大,导出后的csv比excel大还是小不好说,以实际操作为准
下面是表格对比两者差异,看着更直接。
main.py中调用async_execute_csv是在tools.py中写了个多线程打表,提升效率,其本质还是调用了 main.py中的execute_csv函数,函数内部继续调用export_csv.execute开始执行export_csv.py文件中的execute函数。
下面是export_csv.py文件全部代码
import traceback
import os
import sys
import xlrd
import codecs
import unicodecsv
import debug
import checker
import setting
import Parser
from tableData import tableManager
import excelCellData
import re
import checkAll
import globValue
import json
import start
def getTableColCount(sheet):
title = []
try:
for i in range(100):
title.append(sheet.cell_value(3, i))
except:
return i
return 0
def getTableName(sheet):
if(sheet.cell(1,0).ctype == 0):
return None
return sheet.cell_value(1,0)
def getTablePart(sheet):
if(sheet.cell(1, 1).ctype == 0):
return None
return int(sheet.cell_value(1, 1))
def getTableUser(sheet, endCol):
firstCell = sheet.cell(2, 0).value
for i in range(1, endCol):
if(sheet.cell(3, i).ctype == 0):
continue
if(sheet.cell(2, i).value != firstCell):
return u"common"
if(firstCell == "c"):
return u"client"
elif(firstCell == "s"):
return u"server"
return u"common"
def getTableKey(sheet):
if(sheet.cell(4, 0).ctype == 0):
return None
return sheet.cell_value(4, 0)
def toUTF8(data):
if not (type(data) is unicode):
data = unicode(data)
return data.encode('utf-8')
# 导出XML
def execute(excelName):
return_csvs = []
xlrd.Book.encoding = "gbk"
excelPath = setting.dirPath_excel + excelName
# 打开Excel
excel = xlrd.open_workbook(excelPath)
# 遍历子表,导出XML
for sheet_name in excel.sheet_names():
# 检测表名,是否符合规范,不符合则跳过
if(sheet_name[-1] != "="):
name = sheet_name.encode('utf-8').decode('unicode_escape')
continue
# 打开一个表单
sheet = excel.sheet_by_name(sheet_name)
checkTableHead(sheet, debug.toUTF8(excelName), toUTF8(sheet_name))
# 第三行开始是字段
# 第七行开始是数据
# 导出每个子表
tableName = getTableName(sheet)
csvName = tableName
tablePart = getTablePart(sheet)
if(tablePart is not None):
csvName = "{}_{}".format(tableName, tablePart)
return_csvs.append("{}".format(csvName))
# 当前sheet的列数
endCol = getTableColCount(sheet)
filePath = setting.dirPath_csv + csvName + ".csv"
fileObj = codecs.open(filePath, "wb")
fileObj.seek(0)
csv_writer = unicodecsv.writer(fileObj, encoding='utf-8-sig')
# 写入配表使用者
tableUser = getTableUser(sheet, endCol)
csv_writer.writerow([tableUser, excelName.decode("gbk"), sheet_name])
# 如果一行数据都没有,写入一行空数据
fillEmpty = False
rowCount = sheet.nrows
if rowCount < 9:
fillEmpty = True
rowCount = 9
# 每个子表每行数据
for i in range(8, rowCount):
rowData = excelCellData.ExcelRowData(debug.toUTF8(excelName), toUTF8(sheet_name), i)
# 每个子表每列数据
for j in range(0, endCol): # endCol
if(sheet.cell(3, j).ctype == 0):
continue
sign = toUTF8(sheet.cell(2, j).value)
title = toUTF8(sheet.cell(3, j).value)
word = toUTF8(sheet.cell(4, j).value)
link = toUTF8(sheet.cell(7, j).value) #检测是否有link标识
cellData = toUTF8(readCell(sheet, i, j, fillEmpty))
rowData.addCellData(sign, title, word, cellData, i, j, i == 8)
if globValue.getIsCheck() == 1:
if link != "" and link != "__options__" and link.startswith("link"): #拿到要Link的数据
link = re.split(':|:',link)[1]
if title == "INT":
if len(cellData) != 0:
if(len(cellData) >= 3 and cellData[-2:] == ".0"):
cellData = cellData[:-2]
if cellData != '-1' and cellData != '0' and cellData != '':
if checkAll.CheckLink(link,cellData) == 0:
debug.error("表{}的子表[{}]中第{}行要link的ID:{}在对应的表[{}]中不存在\n".format(debug.toUTF8(excelName),csvName,i+1,cellData,link))
elif title == "INT[]":
if len(cellData) != 0:
data = cellData.split('*')
for v in data:
if(len(v) >= 3 and v[-2:] == ".0"):
v = cellData[:-2]
if v != '-1' and v != '0' and v != '':
if checkAll.CheckLink(link,v) == 0:
debug.error("表{}的子表[{}]中第{}行要link的ID:{}内的{}在对应的表[{}]中不存在\n".format(debug.toUTF8(excelName),csvName,i+1,cellData,v,link))
elif title == "STRING":
if len(cellData) != 0:
if(len(cellData) >= 3 and cellData[-2:] == ".0"):
cellData = cellData[:-2]
if cellData != '-1' and cellData != '0' and cellData != '':
if checkAll.CheckLink(link,cellData) == 0:
debug.error("表{}的子表[{}]中第{}行要link的ID:{}在对应的表[{}]中不存在\n".format(debug.toUTF8(excelName),csvName,i+1,cellData,link))
elif title == "STRING[]":
if len(cellData) != 0:
data = cellData.split('*')
for v in data:
if v != '-1' and v != '0' and v != '':
if checkAll.CheckLink(link,v) == 0:
debug.error("表{}的子表[{}]中第{}行要link的ID:{}内的{}在对应的表[{}]中不存在\n".format(debug.toUTF8(excelName),csvName,i+1,cellData,v,link))
elif title == "JSON":
data = json.loads(cellData)
for v in data:
for value in v:
value = value.decode("unicode_escape").encode("utf8")
if len(value) != 0:
if checkAll.CheckLink(link,value) == 0:
debug.error("表{}的子表[{}]中第{}行要link的ID:{}在对应的表[{}]中不存在\n".format(debug.toUTF8(excelName),csvName,i+1,value,link))
# 如果是第一行数据
if i == 8:
rowSigns = rowData.getAllCellDataSigns()
csv_writer.writerow(rowSigns) # 写入归属标识
rowTitles = rowData.getAllCellDataTitles()
csv_writer.writerow(rowTitles) # 写入数据类型
rowWords = rowData.getAllCellDataWords()
csv_writer.writerow(rowWords) # 写入数据字段
rowDatas = rowData.getAllCellDataContents()
csv_writer.writerow(rowDatas)
fileObj.close()
# 添加到配表管理器
tableManager.addTableData(tableName, tablePart, getTableKey(sheet), tableUser)
return return_csvs
#检查Excel单元格是否是空的
def isEmpty(sheet_cell):
return sheet_cell.ctype == 0 or sheet_cell.value == u''
#检查表头的合法性
def checkTableHead(sheet, excelName, sheetName):
cell_value = readCell(sheet, 0, 0)
if cell_value != u"TableName":
debug.throwError("表头不合法:Excel:{}分页{} 单元格A1 {} 不是\'TableName\'".format(excelName, sheetName, toUTF8(cell_value)))
cell_value = readCell(sheet, 0, 1)
if cell_value != u"Part":
debug.throwError("表头不合法:Excel:{}分页{} 单元格B1 {} 不是\'Part\'".format(excelName, sheetName, toUTF8(cell_value)))
cell_value = readCell(sheet, 1, 0)
if cell_value == u"":
debug.throwError("表头不合法:Excel:{}分页{} 单元格A2 没有填写表格名称".format(excelName, sheetName))
else:
if checker.isNumber(toUTF8(cell_value)):
debug.throwError("表头不合法:Excel:{}分页{} 单元格A2 表格名称:{} 不能为数字".format(excelName,sheetName,toUTF8(cell_value)))
# 读取单元格数据
# fillEmpty:True 强行返回空
def readCell(sheet, rowx, colx, fillEmpty=False):
if not fillEmpty:
if sheet.cell(rowx, colx).ctype == 0:
return u''
return sheet.cell(rowx, colx).value
return u''
复制代码
下面是我们项目的Excel配表数据
代码核心内容还是readCell读取格子内容并写入到csv格子中,我们项目的配表有很多自己约定俗称的潜规则,由于代码中很多内容和潜规则有关联,我做一下简单介绍。
excel 1-8行是 配置行,从第9行开始是正式数据,因此在代码中for循环我们是从8(读表是从第0行开始,因此8就是第9行)开始循环读取每个cell单元格的数据。
第一行:固定写死的,就是个提示没有意义
第二行:表名以及该表是否有其他部分,如果一个表由多个sheet也组成,则第一个Sheet页Part写1,后面的sheet不用写TableName了,Part的地方写 23456,之后会生成多个csv文件,在外部合并这些数据
第三行:该列数据是给客户端用还是服务器用,用c和s来标注,打表流程中客户端的数据和服务器的数据是分开打的,也就是说同一个csv文件会生成两个bytes文件,这样可以节省不少内存。
第四行:每一列的数据类型,除了标准的STRING INT FLOAT,还支持JSON,INT[] STING[] FLOAT[]数组等格式。之后会介绍数组是如何实现的,但是在该流程中,仅仅是读取每个格子数据并塞入csv,因此不涉及到类型操作。
第五行:每一列数据的变量名,这个没什么好说的,只要该sheet页的变量名不重复就好,重复的话要抛出异常报错。
第六行:变量名的中文解释
第七行:详细描述以及规则
第八行:如果有关联表,如果这一列数据有关联表,则把关联表的名字写上,之后会对该关联表检查是否有重复Id等操作。该行项目没需求可以删掉同时要修改代码从7开始循环遍历。
导出的csv文件用记事本打开是这个样子,也可以用excel打开csv文件,看起来和excel没区别。
导出时要注意输出目录哦。所有表的csv要输出到一个文件夹中
[网络游戏开发]Python打表工具系列[第三篇] [打表流程第一步] 将excel文件转CSV并输出到CSV目录