目前而言,Python 3.2存取XML有以下四种方法:
1.Expat
2.DOM
3.SAX
4.ElementTree
以以下xml作为讨论依据
<?xml version="1.0" encoding="utf-8"?> <Schools> <School Name="XiDian"> <Class Id="030612"> <Student Name="salomon"> <Scores> <Math>98</Math> <English>85</English> <physics>89</physics> </Scores> </Student> <Student Name="Jupiter"> <Scores> <Math>74</Math> <English>83</English> <physics>69</physics> </Scores> </Student> </Class> <Class Id="030611"> <Student Name="Venus"> <Scores> <Math>98</Math> <English>85</English> <physics>89</physics> </Scores> </Student> <Student Name="Mars"> <Scores> <Math>74</Math> <English>83</English> <physics>69</physics> </Scores> </Student> </Class> </School> </Schools>
Expat
Expat是一个面向流的解析器。您注册的解析器回调(或handler)功能,然后开始搜索它的文档。当解析器识别该文件的指定的位置,它会调用该部分相应的处理程序(如果您已经注册的一个)。该文件被输送到解析器,会被分割成多个片断,并分段装到内存中。因此expat可以解析那些巨大的文件。
SAX
SAX是个循序存取XML的解析器API,一个实现SAX的解析器(也就是“SAX Parser”)以一个串流解析器的型式作用,拥有事件驱动API。由使用者定义回调函数,解析时,若发生事件的话会被调用。事件在任一XML特性遇到时引发,以及遇到他们结尾时再次引发。XML属性也作为传给元素事件资料的一部分。SAX 处理时单方向性的;解析过的资料无法在不重新开始的情况下再次读取。
DOM
DOM解析器在任何处理开始之前,必须把整棵树放在内存,所以DOM解析器的内存使用量完全根据输入资料的大小(相对来说,SAX解析器的内存内容,是只基于XML档案的最大深度(XML树的最大深度)和单一XML项目上XML属性储存的最大资料)。
DOM在python3.2中有两种实现方式:
1.xml.minidom是一个基本的实现。
2.xml.pulldom只在需要时构建被访问的子树。
''' Created on 2012-5-25 @author: salomon ''' import xml.dom.minidom as minidom dom = minidom.parse("E:\\test.xml") root = dom.getElementsByTagName("Schools") #The function getElementsByTagName returns NodeList. print(root.length) for node in root: print("Root element is %s。" %node.tagName)# 格式化输出,与C系列语言有很大区别。 schools = node.getElementsByTagName("School") for school in schools: print(school.nodeName) print(school.tagName) print(school.getAttribute("Name")) print(school.attributes["Name"].value) classes = school.getElementsByTagName("Class") print("There are %d classes in school %s" %(classes.length, school.getAttribute("Name"))) for mclass in classes: print(mclass.getAttribute("Id")) for student in mclass.getElementsByTagName("Student"): print(student.attributes["Name"].value) print(student.getElementsByTagName("English")[0].nodeValue) #这个为什么啊? print(student.getElementsByTagName("English")[0].childNodes[0].nodeValue) student.getElementsByTagName("English")[0].childNodes[0].nodeValue = 75 f = open('new.xml', 'w', encoding = 'utf-8') dom.writexml(f,encoding = 'utf-8') f.close()
ElementTree
目前搜到的ElementTree的信息较少,目前不知道其工作机制。有资料显示ElementTree近乎一种轻量级的DOM,但是ElementTree 所有的 Element
节点的工作方式是一致的。
''' Created on 2012-5-25 @author: salomon ''' from xml.etree.ElementTree import ElementTree tree = ElementTree() tree.parse("E:\\test.xml") root = tree.getroot() print(root.tag) print(root[0].tag) print(root[0].attrib) schools = root.getchildren() for school in schools: print(school.get("Name")) classes = school.findall("Class") for mclass in classes: print(mclass.items()) print(mclass.keys()) print(mclass.attrib["Id"]) math = mclass.find("Student").find("Scores").find("Math") print(math.text) math.set("teacher", "bada") tree.write("new.xml")
Compare:
就以上几点来说Expat和SAX解析XML方式相同,就是不知道性能相比怎样。DOM相对于以上两种解析器,消耗内存,而且由于存取耗时,所以处理文件相对来说慢。如果文件太大无法载入内存,DOM这种解析器就不能用了,但是对于,某些种类的XML验证需要存取整份文件,或者某些XML处理仅要求存取整份文件的需求时,DOM是唯一选择。
Note:
需要指出的是存取XML的这几项技术并不是Python独创的,Python也是通过借鉴其他语言或者直接从其他语言引入进来的。例如Expat就是一个用C语言开发的、用来解析XML文档的开发库。而SAX最初是由DavidMegginson采用java语言开发的,DOM可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构。可以应用于任何编程语言。
做为对比我也想列举一下C#存取XML文档的方式:
1. 基于DOM的XmlDocument
2. 基于流文件的XmlTextReader 和 XmlTextWriter(它和SAX流文件实现不同,SAX是事件驱动模型)。
3. Linq to Xml
流文件两种模型:XmlTextReader/XMLTextWriter VS SAX
流模型每次迭代XML文档中的一个节点,适合于处理较大的文档,所耗内存空间小。流模型中有两种变体——“推”模型和“拉”模型。
推模型也就是常说的SAX,SAX是一种靠事件驱动的模型,也就是说:它每发现一个节点就用推模型引发一个事件,而我们必须编写这些事件的处理程序,这样的做法非常的不灵活,也很麻烦。
.NET中使用的是基于“拉”模型的实现方案,“拉”模型在遍历文档时会把感兴趣的文档部分从读取器中拉出,不需要引发事件,允许我们以编程的方式访问文档,这大大的提高了灵活性,在性能上“拉”模型可以选择性的处理节点,而SAX每发现一个节点都会通知客户机,从而,使用“拉”模型可以提高Application的整体效率。
Reference:
第一种方法是:通过project->link->Object/Library Module中加入.lib文件(或者在源代码中加入指令#pragma comment(lib, “Lib.lib”)),并将.dll文件置入工程所在目录,然后添加对应的.h头文件。
#include "stdafx.h" #include "DLLSample.h" #pragma comment(lib, "DLLSample.lib") //你也可以在项目属性中设置库的链接 int main() { TestDLL(123); //dll中的函数,在DllSample.h中声明 return(1); }
(2)显式链接
需要函数指针和WIN32 API函数LoadLibrary、GetProcAddress装载,使用这种载入方法,不需要.lib文件和.h头文件,只需要.dll文件即可(将.dll文件置入工程目录中)。
#include <iostream> #include <windows.h> //使用函数和某些特殊变量 typedef void (*DLLFunc)(int); int main() { DLLFunc dllFunc; HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll"); if (hInstLibrary == NULL) { FreeLibrary(hInstLibrary); } dllFunc = (DLLFunc)GetProcAddress(hInstLibrary, "TestDLL"); if (dllFunc == NULL) { FreeLibrary(hInstLibrary); } dllFunc(123); std::cin.get(); FreeLibrary(hInstLibrary); return(1); }
LoadLibrary函数利用一个名称作为参数,获得DLL的实例(HINSTANCE类型是实例的句柄),通常调用该函数后需要查看一下函数返回是否成功,如果不成功则返回NULL(句柄无效),此时调用函数FreeLibrary释放DLL获得的内存。GetProcAddress函数利用DLL的句柄和函数的名称作为参数,返回相应的函数指针,同时必须使用强转;判断函数指针是否为NULL,如果是则调用函数FreeLibrary释放DLL获得的内存。此后,可以使用函数指针来调用实际的函数。
最后要记得使用FreeLibrary函数释放内存。
注意:应用程序如何找到DLL文件?
使用LoadLibrary显式链接,那么在函数的参数中可以指定DLL文件的完整路径;如果不指定路径,或者进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:
(1)包含EXE文件的目录
(2)工程目录
(3)Windows系统目录
(4)Windows目录
(5)列在Path环境变量中的一系列目录