[Python]_[初级]_[校验XML文件完整性]

本文介绍了一种利用Python进行XML文档校验的方法,重点在于快速定位并修复XML文档中的错误。通过使用xml.sax模块,文章提供了一种高效且节省内存的方式来进行XML文档的有效性检查。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

场景

  1. 在开发生成docx文档时,也需要生成内部的word/document.xml文档, 而生成xml避免不了需要校验xml的元素标签完整性,即开始和结束标签匹配。如果每次生成docx文档还需要解压获取打开document.xml文件来手动判断有效性效率就太低了。前面讲过可以通过python来解压zip(docx)格式, 当然也可以通过python来校验生成的docx文档是否有损坏。

说明

  1. docx实际上就是zip格式, 我们可以把后缀名改为zip,之后就能解压出来一些文件,里面的文字部分就是存储在word/document.xml里的.

  2. 在没有python之前,可以通过微软的XML Notepad免费软件打开xml文档,根据弹出的错误提示窗口来判断XML文档哪里有问题。我这里说的XML文档都是上10M的,用普通的文档编辑器打开是看不出问题的。

  3. python可以使用xml.sax标准库模块来进行校验,速度快,内存使用少,无需生成庞大的DOM对象。

  4. xml.sax的错误提示最重要的是指示某行:某列出现什么错误: 比如
    error.xml:5:6: mismatched tag。但是这个错误提示并不全,最好能给出错误位置的前后100个字符,这样能方便推理出什么原因造成这个XML解析错误的. 我们可以通过错误提示的某行某列,某m行某n列即第m行,第n个字符,注意,并不是第n个字节,这对于中文多字节字符定位更加准确。

  5. 我这里写了一个方法charSeek来读取字符串到某行某列,并输出它的前后x个字符作为输出参考。注意,我们读取文本文件,内部是使用的TextIOBase,它不能使用文件对象的seek来跳转,因为它是跳转字节的;但是可以使用readxx(size)方法,它是读入指定个字符,通过readxx方法,我们可以实现定位到第m行第n列的字符。

例子

XmlValidator.py


from io import SEEK_CUR, SEEK_SET
from xml.sax.handler import ContentHandler
from xml.sax import SAXParseException, make_parser 
import click

class MyContentHandler(ContentHandler):

    def startElement(self, name, attrs):
        self.elementName = name
        self.elementAttrs = attrs
        self.isElementStart = True
        self.elementConent = ""

    def endElement(self, name):
        self.elementName = name
        self.isElementStart = False
        
    def characters(self, content):
        if content != None:
            self.elementConent = content

def printElement(elementName,isStartElement,elementAttris,elementContent):
    print("[<",end="")
    if not isStartElement:
        print("/",end="")
    print(elementName,end="")
    if(elementAttris != None):
        for key,value in elementAttris.items():
            print(" "+key+"="+value,end=" ")
    print(">",end="")
    if(elementContent != None):
        print(elementContent,end="")
    print("]")

def charSeek(file,offset,maxOffsetLength):
    maxBuf = 1024
    if maxBuf > offset:
        maxBuf = offset
    
    pos = 0
    line = ""
    while pos < offset:
        if (pos + maxBuf) > offset:
            maxBuf = offset - pos
        line = file.readline(maxBuf)
        pos = pos + maxBuf
    return line[-maxOffsetLength:]

def readFileLineColumnContext(fileName,lineNumber,columnNumber,maxOffsetLength):
    # print("[File:%s]-[Line:%d]-[Column:%d]-[MaxOffsetLength:%d]" 
    #     % (fileName,lineNumber,columnNumber,maxOffsetLength))

    with open(fileName,encoding="utf-8") as f:
        n = 1
        maxBuf = 1024
        line = ""
        while n < lineNumber:
            n = n +1
            lineEnd = False
            while not lineEnd:
                line = f.readline(maxBuf)
                # print(line)
                a = line[-1:]
                if a == "\n":
                    lineEnd = True
                    break
        
        # f.seek是针对字节的,不要使用,不然如果位置在一个多字节字符的中间,read解码utf-8字符会报错.
        preLine = charSeek(f,columnNumber,maxOffsetLength)
        b1 = f.read(maxOffsetLength)
        print("-->前一行:"+line[-maxOffsetLength:],end="")
        print("-->当前行:" +preLine+b1)

    pass

@click.command(help="""校验XML格式完整性,请使用--help查看帮助\n
    XmlValidator --path XML路径""")
@click.option('--path',default="",help="XML文件路径") 
@click.pass_context 
def parseFile(ctx,path):
    
    if(len(path) == 0):
        print("请输入有效XML路径!")
        return

    parser = make_parser()
    handler1 = MyContentHandler()
    parser.setContentHandler(handler1)
    
    try:
        parser.parse(path)
    except SAXParseException as e:
        print("-->解析错误: ["+str(e)+"]")
        print("-->解析起始元素上下文-1: ",end="")
        printElement(handler1.elementName,handler1.isElementStart,
            handler1.elementAttrs,handler1.elementConent)
        readFileLineColumnContext(path,e.getLineNumber(),e.getColumnNumber(),20)
        
def test():
    return

if __name__ == "__main__":
    print("Begin Parse!")
    parseFile(obj={})  
    print("End Parse!")
    pass

error.xml

<html><div>中国的xxxxx</div>
    <div>x国内国内国内国内国内x国内国内xx</div>
    <p>asdfasdfa国内的.sdfasfa
    </html>

使用和输出

python XmlValidator.py --path error.xml

-->解析错误: [..\tests\resources\error.xml:5:6: mismatched tag]
-->解析起始元素上下文-1: [<p>    ]
-->前一行:sdfasdfa国内的.sdfasfa
-->当前行:    </html>

下载

  1. 使用pyinstaller编译出的独立的EXE程序。AutomaticPython

参考

python检查xml格式正确性

no-module-named-when-using-pyinstaller

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白行微

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值