您的位置:首页 > 其它

excel转xml

2016-08-17 11:12 399 查看
python把excel文件转成xml文件。

策划用excel配置各种数据,程序在使用前,用python把该excel文件转换成xml文件,再在程序里面加载读取xml文件。

首先定好规则,我们是这样的,一个excel文件只允许第一个表格存放有效配置数字,列名字以c_开头表示此列只有客户端需要,以s_开头表示此列只有客户端需要,以_开头表示为无效配置数据(策划解释说明用),这样定好规则后,就能更清晰的知道哪些需要写入xml文件中,还有,规定每一个表格的第二行为对每一列名字的说明。 也就是说 第二行的数据是不要写入xml文件的。比如如下表格:

itemIditemNameIdc_itemColors_itemPrice_comment
物品id物品名字id物品颜色物品价格补充说明
00011000110钻石道具
000210002red50帽子道具
000310003red50鞋子道具
其中单元格为空的表示该行数据没有该列的属性,如表格中钻石道具没有颜色这一属性。

我们用python来实现转换,其实用java c c++ 都可以,但是做些小工具嘛,最好是用轻便而简单的语言实现,因为对效率不是那么的高,转换的时候,多花费个几秒钟也是可以接受的。所以就用python来写咯,对了最近node.js很火,也可以用js来写,原理都是一样的。

因为要读取excel文件,那么首先就要导入xlrd模块, 以下是我写的py代码:

# -*- coding: utf-8 -*-
import os
import sys
import types
import xlrd
import inspect
import shutil
from time import localtime, strftime
EXL_PATH = ''
XML_PATH = ''
TEMP_EXL_PATH = './xls'
TEMP_XML_PATH = './xml'
LOG_PATH = './log'

reload(sys)
sys.setdefaultencoding('utf8')

def getExlList():
exl_list = []
for (dirpath, dirnames, filenames) in os.walk(EXL_PATH):
for filename in filenames:
if os.path.splitext(filename)[1] == '.xls':
exl_list.append(filename)
return exl_list

def isChinese(stringCode):
for ch in stringCode.decode('utf-8'):
if u'\u4e00' <= ch <= u'\u9fff':
return True
return False

def makeDir(dirPath):
if not os.path.exists(dirPath):
os.mkdir(dirPath)

def copyDir(sourceDir, destDir):
if os.path.exists(destDir):
shutil.rmtree(destDir)
shutil.copytree(sourceDir,destDir)

def copyAllFile(sourceDir, destDir):
for(dirpath, dirnames, filenames) in os.walk(sourceDir):
for filename in filenames:
desFileName = destDir + '/' + filename
shutil.copy(sourceDir + '/' + filename,desFileName)

class enumColType:
clientOnly = 0           #客户端专用
serverOnly = 1           #服务器专用
cleAndSer  = 2           #cs都用
comment    = 3           #cs都无需用

def getColType(sourceString):
if sourceString[0] == '_':
return enumColType.comment
elif sourceString[0:2] == 'c_':
return enumColType.clientOnly
elif sourceString[0:2] == 's_':
return enumColType.serverOnly
else:
return enumColType.cleAndSer

def writeToXml(exlName):
prefixFileName = exlName.split('.')[0]
xmlFileName = prefixFileName + '.xml'
xmlServerFileFullName = TEMP_XML_PATH + '/server/' + xmlFileName
xmlClientFileFullName = TEMP_XML_PATH + '/client/' + xmlFileName

fileClient = open(xmlClientFileFullName,'wb')
fileClient.write('<?xml version="1.0" encoding="utf-8" ?>\n')
fileClient.write('<root>\n')

fileServer = open(xmlServerFileFullName,'wb')
fileServer.write('<?xml version="1.0" encoding="utf-8" ?>\n')
fileServer.write('<root>\n')

exlFileFullName = TEMP_EXL_PATH + '/' + exlName

exlData = xlrd.open_workbook(exlFileFullName,formatting_info = True,encoding_override="utf-8")
table = exlData.sheets()[0]

nrows = table.nrows
ncols = table.ncols
print 'nrows:' + str(nrows) + ' ncols:' + str(ncols)
fileLog.write('xlsName:%s, nrows:%s, ncols:%s\n' %(exlName,str(nrows),str(ncols)))

firstRows = table.row_values(0)

for rownum in range(2,nrows):
stringClient = '<element'
stringServer = '<element'
row = table.row_values(rownum)
for colnum in range(ncols):
if row[colnum] != '' and firstRows[colnum] != '':                 #单元格不为空 且列名不为空
if type(row[colnum]) is types.FloatType:
row[colnum] = int(row[colnum])

colType = getColType(firstRows[colnum])

if  colType == enumColType.cleAndSer:     #cs 都用
tempString = ' %s="%s"' %( str(firstRows[colnum]), str(row[colnum]) )
stringClient += tempString
stringServer += tempString
elif colType == enumColType.clientOnly:    #只客户端用
ss = str(firstRows[colnum])[2:]
tempString = ' %s="%s"' %( ss, str(row[colnum]) )
stringClient += tempString
elif colType == enumColType.serverOnly:     #服务器专用
ss = str(firstRows[colnum])[2:]
tempString = ' %s="%s"' %( ss, str(row[colnum]) )
stringServer += tempString

stringClient += '/>\n'
fileClient.write(stringClient)

stringServer += '/>\n'
fileServer.write(stringServer)

fileClient.write('</root>')
fileClient.close()

fileServer.write('</root>')
fileServer.close()

fileLog.write('xlsName:%s transform success\n' %(exlName))

if __name__ == "__main__":
EXL_PATH =  sys.argv[1]
XML_PATH =  sys.argv[2]
print 'EXL_PATH = ' + EXL_PATH
print 'XML_PATH = ' + XML_PATH

makeDir(LOG_PATH)
copyDir(EXL_PATH,TEMP_EXL_PATH)

makeDir(TEMP_XML_PATH)
makeDir(TEMP_XML_PATH + '/client')
makeDir(TEMP_XML_PATH + '/server')

fileLog = open(LOG_PATH + '/' + strftime("%Y%m%d%H%M%S", localtime()) + '.txt', "wb")
fileLog.write('from %s to %s\n' %(EXL_PATH,XML_PATH))

allExlFiles = getExlList()
for(exlFile) in allExlFiles:
print 'exchange file : ' + exlFile
writeToXml(exlFile)

copyAllFile(TEMP_XML_PATH + '/client',XML_PATH)
fileLog.close()


具体就是 读取excel的第一个表格,然后遍历每一行,分情况,把要写入文件字段累加,最后写入xml文件,代码很简单,但是里面包括了一些和工程涉及的东西,不过很容易看懂,也不影响。

那么上面的表格经过转换之后,就会得到两个文件,一个是给服务器的,一个是给客户端的。其中服务器xml如下:

<?xml version="1.0" encoding="utf-8" ?>
<root>
<element itemId="0001" itemNameId="10001" itemPrice="10"/>
<element itemId="0002" itemNameId="10002" itemPrice="50"/>
<element itemId="0003" itemNameId="10003" itemPrice="50"/>
</root>


客服端xml文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<root>
<element itemId="0001" itemNameId="10001"/>
<element itemId="0002" itemNameId="10002" itemColor="red"/>
<element itemId="0003" itemNameId="10003" itemColor="red"/>
</root>


注意这两个文件内容的区别 和excel之前的区别。

这样就实现了策划按照规定来填写表格,我们程序负责转化就是了。

改进和优化:有个时候,表格行数很多,就会有一个问题,什么问题,相同的字符串太多,因为每一行的属性名字就是每一列的名字,比如上面的表格,如果有1000行,那么这个xml中就会有1000 个 字符串 “itemId”,并且其余的属性也基本那么多,这样一来就会想到,压缩了,其实zip文件压缩的最基本的原理就是重复多次出现的字符串,用一个标识代替。 我们可以首先给每一列的名字,用较短的字符串来表示,比如第一列用c0代替,如上xml可以这么写:

<?xml version="1.0" encoding="utf-8" ?>
<root>
<column c0="itemId" c1="itemNameId" c2="itemPrice" c3="itemColor"/>
<element c0="0001" c1="10001"/>
<element c0="0002" c1="10002" c3="red"/>
<element c0="0003" c1="10003" c3="red"/>
</root>


如果表格中行数较少,影响不大,但是表格行数多了,效果就显示出来了,无论是xml文件的大小,还有在内存中解析所占的内存,都是一份不小的优化了。 至于具体怎么实现,这里就不贴代码了,首先写入xml的原理无非新建一个列的索引,把第一列名写到column。 再以后写内容的时候,不写列名,而写列的索引。 至于解析也一样,读到某一行数据 再把属性索引对应到colum 得到真正的属性名字,就ok了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  excel