excel转xml

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了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值