7-29<X10>kpg.xml 脚本和流
Contents
- “接收……作为参数”一律改为“接受……作为参数”
本章标题:Scrips 和 Streams
脚本和流
10.1 抽象输入源
Para 1:Python 的最强大力量之一是它的动态绑定,并且动态绑定最强大的用法之一是类文件(file-like)对象。
Python 的最强大力量之一是它的动态绑定,而动态绑定最强大的用法之一是类文件(file-like)对象。
例10.1上, Para -2:只要你将一个类文件对象传递给函数,函数只是调用对象的 read 方法,函数可以处理任何类型的输入源,而不需要处理每种类型的特定代码。
只要你将一个类文件对象传递给函数,函数只是调用对象的 read 方法,就可以处理任何类型的输入源,而不需要为处理每种类型分别编码。
例10.1上, Para -1:你可能纳闷过这和 XML 处理有什么关系,……
你可能会纳闷,……
例10.1, (3):确保处理完成
文件后调用 close 方法。
例10.2上:哦,所有这些看上去象是在浪费大量的时间。毕竟,你已经看过 minidom.parse 可以只接收文件名,并自动执行所有打开文件和关闭无用文件的行为。不错,如果你知道正要解析的是一个本地文件,你可以传递文件名而且 minidom.parse 可以足够聪明的做正确的事情™,这一切都不会有问题。但是请注意,使用类文件分析直接从Internet上来的 XML 文档是多么相似和容易的事情!
哦,所有这些看上去像是在浪费大量的时间。毕竟,你已经看到,minidom.parse 可以只接收文件名,并自动执行所有打开文件和关闭无用文件的行为。不错,如果你知道正要解析的是一个本地文件,你可以传递文件名而且 minidom.parse 可以足够聪明地做正确的事情(Do The Right Thing™),这一切都不会有问题。但是请注意,使用类文件,会使分析直接从 Internet 上来的 XML 文档变得多么相似和容易!
我就纳闷了,怎么“做正确的事情”也成了 trademark。顺便加了个注释
这是一部著名的电影。——译注
例10.2, (1):
正如在前一章中所看到的,……
例10.2, (4):By the way, this URL is real, and it really is XML. It's an XML representation of the current headlines on Slashdot, a technical news and gossip site.
顺便提一句,这个 URL 是真实的,它真的是一个 XML。它是Slashdot站点(这是一个技术新闻和随笔站点)上当前标题的 XML 表示。
顺便提一句,这个 URL 是真实的,它真的是一个 XML。它是 Slashdot 站点(一个技术新闻和随笔站点)上当前新闻提要的 XML 表示。
例10.3下:OK,所以你可以使用 minidom.parse 函数来解析本地文件和远端 URL,但对于解析字符串,你使用……一个不同的函数。
好吧,所以你可以使用 minidom.parse 函数来解析本地文件和远端 URL,但对于解析字符串,你使用……另一个函数。
例10.4上:如果有一个方法可以把字符串转换成类文件对象,那么你可以只把
这个对象传递给 minidom.parse 就可以了。
只要
例10.4, (1):StringIO 模块只包含了单个
类,也叫 StringIO,它允许你将一个字符串转换为一个类文件对象。这个
StringIO 类在创建实例的时候接收字符串作为参数。
一个
例10.4, (4):任何时候,read 都将返回字符串的未读剩余
部分。
例10.5, (1):……它将调用对象的 read 方法并高兴的开始解析,绝不会知道它的输入源源
自一个硬编码的字符串。
例10.5下:那么现在你知道了如何使用单个
函数,minidom.parse,来解析一个保存在 web 页面上、本地文件中或硬编码字符串中的 XML 文档。
同一个
例10.6, (1):……source 是某种类型的字符串;它可能是一个 URL(例如 'http://slashdot.org/slashdot.rdf' ),一个本地文件的完整或者部分路径名(例如 'binary.xml'),或者是一个包含了需要解析
XML 数据的字符串。待解析
例10.6, (2):This is actually elegant in the sense that, if urllib ever supports new types of URLs in the future, you will also support them without recoding.
这里通过强制方式进行:尝试把它当作一个 URL 打开并静静地忽略打开非 URL 引起的错误。感觉上这样做非常好,因为如果 urllib 将来支持更多的 URL 类型,不用重新编码就可以支持它们。
例10.6, (3):再一次,你不用做任何特别的事来检查 source 是否是一个有效的文件名(总之在不同的平台上,判断文件名有效性的规则变化很大,那么不管怎样做都可能会判断错)。
再一次,你不用做任何特别的事来检查 source 是否是一个有效的文件名(在不同的平台上,判断文件名有效性的规则变化很大,因此不管怎样做都可能会判断错)。
例10.6, (4):到这里,你需要假设 source 是一个其中有硬编码数据的字符串(因为没有什么可以判断的了),所以你可以使用 StringIO 从中创建一个类文件对象并将它返回。(实际上,由于使用了 str 函数,所以 source 没有必要一定是字符串;它可以是任何对象,你可以使用它的字符串表示形式,通过它的定义的 __str__ 特殊方法。)
到这里,你需要假设 source 是一个其中有硬编码数据的字符串(因为没有别的可以判断的了),所以你可以使用 StringIO 从中创建一个类文件对象并将它返回。(实际上,由于使用了 str 函数,所以 source 没有必要一定是字符串;它可以是任何对象,你可以使用它的字符串表示形式,只要定义了它的 __str__ 专用方法。)
例10.6下:……构造一个函数,接收一个指向 XML 文档的 source,而且无需知道这个 source 的含义,并
解析它。
然后
10.2 标准输入、输出和错误
Para 2:……当你 print 某些东西时,结果前往 stdout 管道;当你的程序崩溃并打印出调试信息(类似于
Python 中的
traceback(错误跟踪))的时候,信息前往 stderr 管道。
例如
例10.8, (1):正如
在例 6.9, “简单计数”中看到的,……
例10.8, (3):像 stdout
,stderr 并不为你添加硬回车;如果需要,要自己加上。
和 stdout 一样
例10.8下:……然而,它们仍然是类文件对象,并且你可以将其它任何文件或者类文件对象赋值给它们来重定向它们的输出。
然而,它们仍然是类文件对象,因此你可以将其它任何(类)文件对象赋值给它们来重定向其输出。
例10.9:在 Windows 上,你可以使用 type 来代替 cat 显示文件的内容。
在 Windows 上,你要使用 type 来代替 cat 显示文件的内容。
例10.9, (4):将所有后续的输出重定向到刚才打开的新文件上。
所有后续的输出都会被重定向到刚才打开的新文件上。
例10.9, (5):这样只会将输出结果“printed
”到日志文件中;……
打印
例10.10上:重定向 stderr 以完全相同的方式进行,用 sys.stderr 代替 sys.stdout。
重定向 stderr 以完全相同的方式进行,只要把 sys.stdout 改为 sys.stderr。
例10.10, (4):Also note that you're not explicitly closing your log file, nor are you setting stderr back to its original value. This is fine, since once the program crashes (because of the exception), Python will clean up and close the file for us, and it doesn't make any difference that stderr is never restored, since, as I mentioned, the program crashes and Python ends. Restoring the original is more important for stdout, if you expect to go do other stuff within the same script afterwards.
还要注意你既没有显式关闭日志文件,也没有将 stderr 设回最初的值。这样挺好,因为一旦程序崩溃(由于引发的异常),Python 将替我们清理并关闭文件,这和永远不恢复 stderr 不会造成什么不同,因为,我提到过,一旦程序崩溃,则 Python 结束。如果你希望在同一个脚本的后面去做其它的事情,恢复初始值对 stdout 更为重要。
还要注意你既没有显式关闭日志文件,也没有将 stderr 设回最初的值。这样挺好,因为一旦程序崩溃(由于引发的异常),Python 将替我们清理并关闭文件,因此永远不恢复 stderr 不会造成什么影响。然而对于 stdout,恢复初始值相对更为重要——你可能会在后面再次操作标准输出。
原文比较长,所以 Mark 反复强调了程序崩溃后 Python 的行为。译文相对简洁,就没有这个必要了。
例10.11上:向标准错误写入错误信息是很常见的,所以有一种较快的语法可以立刻信息导出
。
导出信息
例10.11, (1):This shorthand syntax of the print statement can be used to write to any open file, or file-like object. In this case, you can redirect a single print statement to stderr without affecting subsequent print statements. print 语句的快捷语法可以用于向任何打开的文件写入,或者是类文件对象。在这种情况下,你可以将单个 print 语句重定向到 stderr 而且不用影响后面的 print 语句。
print 语句的快捷语法可以用于写入任何打开的文件(或者是类文件对象)。在这里,你可以将单个 print 语句重定向到 stderr 而且不用影响后面的 print 语句。
in this case 指上文的那一行语句。
例10.11下:标准输出,另一方面,只是一个只读文件对象,它表示从前一个程序到这个程序的数据流。这个对于老的 Mac OS 用户和 Windows 用户可能不太容易理解,除非你受到过 MS-DOS 命令行的影响。它工作的方式是你可以在单个命令行中构造一个命令的链,……
另一方面,标准输入是一个只读文件对象,它表示从前一个程序到这个程序的数据流。这个对于老的 Mac OS 用户和 Windows 用户可能不太容易理解,除非你受到过 MS-DOS 命令行的影响。在 MS-DOS 命令行中,你可以使用一行指令构造一个命令的链,……
例10.12, (3):该处打印 binary.xml 的内容,但是“|”字符,称为“管道”符,说明内容不会打印到屏幕上。而且,它们会成为下一个命令的标准输入,在这个例子中是你调用的 Python 脚本。
该处打印 binary.xml 的内容,但是“|”字符,称为“管道”符,说明内容不会打印到屏幕上;它们会成为下一个命令的标准输入,在这个例子中是你调用的 Python 脚本。
例10.12, (4):但是想想这里的扩展性。代替cat binary.xml,你可以通过运行一个脚本动态生成语法,然后你可以通过管道将它导入你的脚本。它可以来源于任何地方:数据库,或者是生成语法的元脚本,或者其他。你根本不需要修改你的 kgp.py 脚本就可以混合使用这个功能。
但是想想这里的扩展性。让我们把 cat binary.xml 换成别的什么东西——例如运行一个脚本动态生成语法——然后通过管道将它导入你的脚本。它可以来源于任何地方:数据库,或者是生成语法的元脚本,或者其他。你根本不需要修改你的 kgp.py 脚本就可以并入这个功能。
例10.13, (1):……所有你要做的就是在函数的开始加入 3 行代码来检测源是否是“-”;如果是,返回 sys.stdin。实际上,that's it!
就这么简单!
10.3 查询缓冲节点
本节标题:缓冲节点查询
查询缓冲节点
Para 1:kgp.py 使用了多种技巧,对你进行 XML 处理而言它们或许有用。第一个就是,使用输入文档的结构稳定特征来构建节点缓冲。
kgp.py 使用了多种技巧,在你进行 XML 处理时,它们或许能派上用场。第一个就是,利用输入文档的结构稳定特征来构建节点缓冲。
Para 2:……每个 ref 包含了一个或者多个 p 元素,p 元素可以包含很多不同的东西,包括 xref。无论何时你遇到一个 xref,都可以通过相同的 id 属性找到相对应的 ref 元素,并选择 ref 元素的子元素之一进行解析。
……每个 ref 包含了一个或多个 p 元素,p 元素则可以包含很多不同的东西,包括 xref。对于每个 xref,你都能找到相对应的 ref 元素(它们具有相同的 id 属性),然后选择 ref 元素的子元素之一进行解析。
Para 3:如何构建语法:为最小的片段定义 ref 元素,然后通过 xref 定义“包含”第一个 ref 元素的 ref 元素,等等。然后,解析“最大的”引用并跟在每个 xref 后面,最后输出真实的文本。
语法的构建方式如下:先为最小的片段定义 ref 元素,然后使用 xref 定义“包含”第一个 ref 元素的 ref 元素,等等。然后,解析“最大的”引用并跟着 xref 跳来跳去,最后输出真实的文本。
例10.14, (2):……返回所有特定名称元素的一个列表。你可以很容易地得到所有 ref 元素的一个列表,然后仅仅是
遍历这个列表。
10.4 查找节点的直接子节点
例10.16, (2):However,……
然而,……
例10.16, (3):每个节点都有一个 nodeType 属性,它可以是元素节点, 文本节点, 注释节点,或者任意数量的其它值。可能值的完整列表在xml.dom包的 init.py 文件中。(关于包更多的,参见9.2节, “包”。)但你只是对元素节点有兴趣,所以你可以过滤出一个列表,其中只包含 nodeType 是元素节点的节点。
每个节点都有一个 nodeType 属性,它可以是ELEMENT_NODE, TEXT_NODE, COMMENT_NODE,或者其它值。可能值的完整列表在 xml.dom 包的 init.py 文件中。(关于包的介绍,参见9.2节, “包”。)但你只是对元素节点有兴趣,所以你可以过滤出一个列表,其中只包含 nodeType 是ELEMENT_NODE的节点。
例10.16, (4):……将会是 p 元素的一个列表,并且 chosen 将以被赋予其中的一个确切值而结束,而这个值是随即选择的。
……将会是 p 元素的一个列表,而 chosen 将被赋予其中的某一个值,而这个值是随机选择的。
10.5 根据节点类型创建不同的处理器
本节标题:通过节点类型创建独立的处理句柄 Creating separate handlers by node type
根据节点类型创建不同的处理器
Para 1:Document还包含了一个或者多个Element对象(for actual XML tags),其中的每一个可以包含其它的Element对象,Text对象(for bits of text),或者Comment对象(for embedded comments)。
Document 还包含了一个或多个 Element 对象(表示 XML 标记),其中的每一个可以包含其它的 Element 对象、Text 对象(表示文本),或者 Comment 对象(表示内嵌注释)。
例10.17下:好,现在你能够得到任何特定
XML 节点的类名了……
给定
例10.18:parse, 一个通用的
XML 节点分发器
例10.18, (1):First off,
注意……
首先,……
例10.19, (1):……它只是turn around并解析语法文件的根元素。
它只是起到中转作用,转而解析语法文件的根元素。
例10.19, (4):parse_Element方法其实本身就是一个分发器,它基于元素的标记名称。……对其它的标记名称在解析语法文件的时候都可以找到类似的函数(<p>标记,<choice>标记)。
parse_Element 方法其实本身就是一个分发器,一个基于元素的标记名称的分发器。……对其它的标记名称(像<p> 和 <choice>)在解析语法文件的时候都可以找到类似的函数。
10.6 处理命令行参数
- 为了术语的一致,本节的“标志”改为“标记”。
Para 1:Python 完备支持创建在命令行运行的程序,并且连同命令行参数和短长样式来指定各种选项。
Python 完全支持创建在命令行运行的程序,也支持通过命令行参数和短长样式来指定各种选项。
例10.20, (1):每个传递给程序的命令行参数都在sys.argv,它仅仅是一个列表。
每个传递给程序的命令行参数都在 sys.argv 中,而它仅仅是一个列表。
例10.21, (4):标记自身和标记参数只是sys.argv列表中的序列元素。
标记自身和标记参数只是 sys.argv 列表中的一串元素。
例10.21下:但是,but then again, it doesn't look like it's going to be all that easy to actually use it.
但是接下来要实际使用它似乎不那么容易。
例10.22, (1):First off,……
首先
例10.22下, 例10.23上:原稿对参数的解释未译。已译。
短参解释下, Para 1:你选择是否指定它们,它们做某些事情(打印帮助)或者改变状态(关闭调试
)
打开调试
同上:……实际上,它可以是一个文件名或者一个 web 地址,你可能还不知道(后面你会明白的)……
实际上,它可以是一个文件名或者一个 web 地址,这时还不知道(后面会确定)
短参解释down, Para 2:To further complicate things,……
更复杂的是
长参解释下:这里要注意的三件事:
这里有三点要注意:
长参解释下, 3.:这也好;只有-d才会打开调试。
这很好;只有 -d 才会打开调试。
例10.23, (4):记得,-d标记没有相应的长标志,……
别忘了,-d 标志没有相应的长标志
例10.23, (5):如果你找到了一个语法文件,-g标志或者--grammar标志带着的,那你要保存跟在它(保存在arg)后面的参数到变量grammar中,覆盖掉在main函数你初始化的默认值。
如果你找到了一个语法文件,跟在 -g 或者 --grammar 标志后面,那你就要把跟在后面的参数(arg)保存到变量grammar 中,覆盖掉在 main 函数你初始化的默认值。
例10.23, (6):That’s it。……并且source将以空字符串结束。
就是这样。……而 source 将是空字符串。
10.7 全部放在一起
Para 3:……然后将语法文件和源文件传给它……
然后将语法文件和源文件传给它
Para 5:……这些引用
都只是 XML 文档中的元素
Para 6:……否则你将打开语法
文件查找……
Para -5:You bounce through the grammar, parsing all the children of each p element,
通过语法的反弹,解析所有 p 元素的孩子,
你在语法里面跳来跳去,解析每一个 p 元素的所有孩子,
Para -2:Eventually, you parse your way down to plain text,
最后,你以你的方式进行解析直到普通文本。就这样一直解析,最后得到普通文本。
Para -1:你打印出来的。
把结果打印出来。
10.8 小结
Para 1:Python 带有解析和操作 XML 文档非常强大的库。这个
minidom 接收一个 XML 文件并将其解析为 Python 对象,……