zoukankan      html  css  js  c++  java
  • python读取,写入和更新xml文件

    VOC数据集的标注文件都是xml格式,最近需要频繁处理xml文件的读取和更新,整理下python处理xml文件的方法。

      XML 指可扩展标记语言(eXtensible Markup Language), 被设计用来传输和存储数据。python中有三个模块解析xml文件:DOM, ElementTree,SAX

    1. DOM(Document Object Model)

      DOM是个跨平台的标准模型,W3C定义了DOM模型里的概念。DOM将XML数据在内存中解析成一个树,通过对树的操作来操作XML。python的xml.dom.minimom模块实现了DOM

    1.1 DOM写入xml文件

           DOM写入xml文件主要是创建dom树,然后创建根结点,创建子节点并加入到根节点,最后将整个dom树写入文件,相关API如下:

    DOM写入相关API
    
    doc = minidom.Document()  #创建树
    doc.createElement("folder")  #创建名为folder的结点
    doc.createTextNode('user')   #创建文本结点,文本为 user
    root_node.appendChild(folder_node) #root_node结点添加folder_node为子节点

    book_node.setAttribute('price','199') #book_node设置属性值
    # 每一个结点对象(包括dom对象本身)都有输出XML内容的方法,如:toxml()--字符串, toprettyxml()--美化树形格式。 doc.toxml(encoding="utf-8") # 输出字符串 doc.toprettyxml(indent='', addindent='	', newl='
    ', encoding="utf-8")) #输出带格式的字符串 doc.writexml() #将prettyxml字符串写入文件

     下面为写入一个VOC标注文件示例代码

    def write_xml():
        #1. 创建dom树对象
        doc = minidom.Document()
    
        #2. 创建根结点,并用dom对象添加根结点
        root_node = doc.createElement("annotation")
        doc.appendChild(root_node)
    
        #3. 创建结点,结点包含一个文本结点, 再将结点加入到根结点
        folder_node = doc.createElement("folder")
        folder_value = doc.createTextNode('user')
        folder_node.appendChild(folder_value)
        root_node.appendChild(folder_node)
    
        filename_node = doc.createElement("filename")
        filename_value = doc.createTextNode('0000001.jpg')
        filename_node.appendChild(filename_value)
        root_node.appendChild(filename_node)
    
        path_node = doc.createElement("path")
        path_value = doc.createTextNode('/home')
        path_node.appendChild(path_value)
        root_node.appendChild(path_node)
    
        source_node = doc.createElement("source")
        database_node = doc.createElement("database")
        database_node.appendChild(doc.createTextNode("Unknown"))
        source_node.appendChild(database_node)
        root_node.appendChild(source_node)
    
        size_node = doc.createElement("size")
        for item, value in zip(["width", "height", "depth"], [1920, 1080, 3]):
            elem = doc.createElement(item)
            elem.appendChild(doc.createTextNode(str(value)))
            size_node.appendChild(elem)
        root_node.appendChild(size_node)
    
        seg_node = doc.createElement("segmented")
        seg_node.appendChild(doc.createTextNode(str(0)))
        root_node.appendChild(seg_node)
    
        obj_node = doc.createElement("object")
        name_node = doc.createElement("name")
        name_node.appendChild(doc.createTextNode("boat"))
        obj_node.appendChild(name_node)
    
        pose_node = doc.createElement("pose")
        pose_node.appendChild(doc.createTextNode("Unspecified"))
        obj_node.appendChild(pose_node)
    
        trun_node = doc.createElement("truncated")
        trun_node.appendChild(doc.createTextNode(str(1)))
        obj_node.appendChild(trun_node)
    
        trun_node = doc.createElement("difficult")
        trun_node.appendChild(doc.createTextNode(str(0)))
        obj_node.appendChild(trun_node)
    
        bndbox_node = doc.createElement("bndbox")
        for item, value in zip(["xmin", "ymin", "xmax", "ymax"], [103, 1, 634, 402]):
            elem = doc.createElement(item)
            elem.appendChild(doc.createTextNode(str(value)))
            bndbox_node.appendChild(elem)
        obj_node.appendChild(bndbox_node)
        root_node.appendChild(obj_node)
    
    
        with open("0000001.xml", "w", encoding="utf-8") as f:
            # 4.writexml()第一个参数是目标文件对象,第二个参数是根节点的缩进格式,第三个参数是其他子节点的缩进格式,
            # 第四个参数制定了换行格式,第五个参数制定了xml内容的编码。
            doc.writexml(f, indent='', addindent='	', newl='
    ', encoding="utf-8")
    
        # 每一个结点对象(包括dom对象本身)都有输出XML内容的方法,如:toxml()--字符串, toprettyxml()--美化树形格式。
        # print(doc.toxml(encoding="utf-8"))  # 输出字符串
        # print(doc.toprettyxml(indent='', addindent='	', newl='
    ', encoding="utf-8"))   #输出带格式的字符串
        # doc.writexml() #将prettyxml字符串写入文件
    写入xml文件

    1.2 读取和更新xml文件

      解析xml文件为DOM树,获取树的根节点,随后即可通过根节点寻找相关的子节点,并获取相关的属性和文本,相关API如下:

    读取xml的API
    
    doc = minidom.parse(xml_path)  #解析xml文件(句柄或文件路径)
    doc = minidom.parseString()  #解析xml字符串
    root_node = doc.documentElement  #获得根节点
    
    print(root_node.nodeName)      #结点名称
    print(root_node.nodeType)       #结点类型  (元素结点,文本结点,属性结点)
    print(root_node.childNodes)     #所有子节点,为列表
    print(node.parentNode)       # 获取父节点
    filename_node = root_node.getElementsByTagName('filename')[0]  #通过结点名称寻找结点,返回列表

    #文本结点 filename
    = filename_node.childNodes[0].data #子节点为文本结点,文本结点有data属性即为文本值 #属性结点 # node.getAttribute('price') #属性结点node,获取其price属性

      下面为一个读取xml文件并更新指定结点文本值的代码:

    def read_xml(xml_path):
        with open(xml_path, "r", encoding="utf-8") as f:
            doc = minidom.parse(xml_path)  #解析xml文件(句柄或文件路径)
            #doc = minidom.parseString()  #解析xml字符串
            root_node = doc.documentElement  #获得根节点
    
            #找到xmin结点并更新其对应的文本值
            xmin_node = root_node.getElementsByTagName("xmin")[0]
            print(xmin_node.childNodes[0].data)
            xmin_node.childNodes[0].data = str(200)
            print(xmin_node.childNodes[0].data)
    
        with open(xml_path, "w", encoding="utf-8") as f:
            doc.writexml(f)
    View Code

    2. ElementTree

    ElementTree就像一个轻量级的DOM, Python专有,使用起来更加简单,常用API如下:

    2.1 读取和解析xml文件

      支持遍历结点,查找结点和访问结点,如下所示:

    def element_read_xml(xml_path):
    
        #1. 获取root结点
        tree = ET.parse(xml_path)   #方式一
        root = tree.getroot()
    
        # tree = ET.ElementTree(file=xml_path)  # 方式二
        # root = tree.getroot()
    
        # with open(xml_path, "r", encoding="utf-8") as f:   # 方式三
        #     root = ET.fromstring(f.read())
        print(root)
    
        #2.访问特定结点属性  (属性包括tag, text, attrib)
        #遍历结点, 每一个结点都是一个迭代器,能遍历其子节点
        for i in root:
            print(i.tag, i.attrib)
            for j in i:
                print(j, j.attrib)
    
        #3.下标方式访问子节点
        print(root[0].tag, root[0].attrib, root[0].text)
    
        #4. 查找结点,支持tag名字和xpath语法
        print(tree.find("folder"))  #在当前结点的子节点中寻找标签为folder的子节点
        print(tree.find(".//name")) #寻找所有子孙结点中第一个标签为name的子节点
        print(tree.findall(".//name"))  #寻找所有子孙结点中标签为name的子节点(返回列表)
        print(tree.findtext(".//name"))  #寻找所有子孙结点中第一个标签为name的子节点,并返回其text属性

    2.2 创建xml文件并写入

      需要创建根结点,然后添加子节点,最后创建节点树并写入,如下所示:

    def write_xml():
        root = ET.Element("node")
        folder_node = ET.Element("folder")
        folder_node.text = "/home"
        folder_node.tail = "
    "
        print(dir(folder_node))
        root.append(folder_node)   #添加子节点
    
        #extend(subments) #添加多个子节点
        elem3 = ET.Element("test_extend")
        elem3.text = "elem 3"
        elem3.tail = "
    "      #结点尾部添加换行
        elem4 = ET.Element("test_extend")
        elem4.text = "elem 4"
        elem4.tail = "
    "
        root.extend([elem3, elem4])
        #insert(index, subment)  #插入子节点
        #remove(subment)      #删除子节点
    
        folder_node = ET.SubElement(root, "folder")  # 为root添加子节点
        folder_node.text = "/home"
    
        tree = ET.ElementTree(root)
        tree.write("output.xml", encoding="utf-8", xml_declaration=True)  #保存时无缩进,添加缩进需要借用dom
    
    
        #借用dom,添加缩进
        # rawtext = ET.tostring(root)
        # dom = minidom.parseString(rawtext)
        # with open("output.xml", "w") as f:
        #     dom.writexml(f, indent="	", newl="", encoding="utf-8")

    2.3 读取并更新结点

      除了下面修改结点的名称外,还可以进行添加子节点,删除子节点等操作

    def update_xml():
        #查找节点并更新
        root = ET.parse("output.xml")
        for node in root.findall(".//folder"):
            if node.text == "/home":
                node.tag = "path"
        ET.dump(root)  #打印xml
        root.write("output.xml")

    3. SAX(Simple API for XML)

      而SAX是一种基于事件的流式处理模型,可以在只读入部分XML的情况下进行处理, 比较快,占用内存少。DOM与etree一般都比SAX简单,  但其将XML数据映射到内存中的树,比较慢,且较耗内存;如果XML文件比较大,或要求速度快时,SAX比较适合;
      利用SAX解析XML文档牵涉到两个部分:解析器和事件处理器。解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件;而事件处理器则负责对事件作出相应,对传递的XML数据进行处理

    3.1 解析器:

    #创建解析器
    #1
    xml.sax.parse( xmlfile, contenthandler[, errorhandler])
    xmlfile - xml文件名
    contenthandler - 必须是一个ContentHandler的对象
    errorhandler - 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象
    
    #2
    xml.sax.parseString(xmlstring, contenthandler[, errorhandler])
    xmlstring - xml字符串
    contenthandler - 必须是一个ContentHandler的对象
    errorhandler - 如果指定该参数,errorhandler必须是一个SAX ErrorHandler对象
    
    #3
    xml.sax.make_parser()

    3.2 事件处理器:

    class EventHandler(sax.ContentHandler):
        def __init__(self):
            pass
    
        #文档启动的时候调用
        def startDocument(self):
            pass
    
        #解析器到达文档结尾时调用
        def endDocument(self):
            pass
    
        # 遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。
        def startElement(self, name, attrs):
            pass
    
        #遇到XML结束标签时调用。
        def endElement(self, tag):
            pass
    
        #标签之间的内容处理时调用
        def characters(self, content):
            pass

      使用示例参考:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    
    import xml.sax
    
    class MovieHandler( xml.sax.ContentHandler ):
       def __init__(self):
    ​      self.CurrentData = ""
    ​      self.type = ""
    ​      self.format = ""
    ​      self.year = ""
    ​      self.rating = ""
    ​      self.stars = ""
    ​      self.description = ""
    
       # 元素开始事件处理
       def startElement(self, tag, attributes):
    ​      self.CurrentData = tag
    ​      if tag == "movie":
    ​         print "*****Movie*****"
    ​         title = attributes["title"]
    ​         print "Title:", title
    
       # 元素结束事件处理
       def endElement(self, tag):
    ​      if self.CurrentData == "type":
    ​         print "Type:", self.type
    ​      elif self.CurrentData == "format":
    ​         print "Format:", self.format
    ​      elif self.CurrentData == "year":
    ​         print "Year:", self.year
    ​      elif self.CurrentData == "rating":
    ​         print "Rating:", self.rating
    ​      elif self.CurrentData == "stars":
    ​         print "Stars:", self.stars
    ​      elif self.CurrentData == "description":
    ​         print "Description:", self.description
    ​      self.CurrentData = ""
    
       # 内容事件处理
       def characters(self, content):
    ​      if self.CurrentData == "type":
    ​         self.type = content
    ​      elif self.CurrentData == "format":
    ​         self.format = content
    ​      elif self.CurrentData == "year":
    ​         self.year = content
    ​      elif self.CurrentData == "rating":
    ​         self.rating = content
    ​      elif self.CurrentData == "stars":
    ​         self.stars = content
    ​      elif self.CurrentData == "description":
    ​         self.description = content
    
    if ( __name__ == "__main__"):
    
       # 创建一个 XMLReader
       parser = xml.sax.make_parser()
       # turn off namepsaces
       parser.setFeature(xml.sax.handler.feature_namespaces, 0)
    
       # 重写 ContextHandler
       Handler = MovieHandler()
       parser.setContentHandler( Handler )
    
       parser.parse("movies.xml")
    View Code

     参考博客:

    https://www.zhihu.com/question/21824329

    https://zhuanlan.zhihu.com/p/85752679

    https://www.cnblogs.com/hupeng1234/p/7262371.html

  • 相关阅读:
    为什么股票一买就跌,一卖就涨?终于找到答案了!
    搜集的一些股票讲师的博客
    一位操盘手的临别赠言
    VMware网络连接 桥接、NAt、host-only模式
    我常用的网络测试工具
    linux下性能测试工具netperf使用
    vm10虚拟机安装Mac OS X10.10教程
    ACE_Svc_Handler 通信原理
    mypwd实现
    2019-2020-1 20175307 20175308 20175319 实验五 通讯协议设计
  • 原文地址:https://www.cnblogs.com/silence-cho/p/12542747.html
Copyright © 2011-2022 走看看