您当前位置:首页 > 深入 Python > 脚本和流 | << >> | ||||
深入 Python从 Python 新手到专家 |
Python 最大的优势之一是其动态绑定,而动态绑定的一种强大用途是 类文件对象。
许多需要输入源的函数可以简单地接收文件名,打开文件进行读取,读取文件,并在完成后关闭文件。但它们并没有这样做。相反,它们接收一个 类文件对象。
在最简单的情况下,类文件对象 是指任何具有 read 方法的对象,该方法带有一个可选的 size 参数,并返回一个字符串。当不带 size 参数调用时,它将从输入源读取所有内容,并将所有数据作为单个字符串返回。当使用 size 参数调用时,它将从输入源读取该数量的数据并返回该数量的数据;当再次调用时,它将从上次停止的地方继续读取并返回下一块数据。
这就是 从真实文件读取 的工作原理;区别在于您不局限于真实文件。输入源可以是任何东西:磁盘上的文件、网页,甚至是硬编码的字符串。只要您将类文件对象传递给函数,并且该函数只调用对象的 read 方法,该函数就可以处理任何类型的输入源,而无需针对每种类型编写特定的代码。
如果您想知道这与 XML 处理有什么关系,minidom.parse 就是这样一个可以接收类文件对象的函数。
>>> from xml.dom import minidom >>> fsock = open('binary.xml')>>> xmldoc = minidom.parse(fsock)
>>> fsock.close()
>>> print xmldoc.toxml()
<?xml version="1.0" ?> <grammar> <ref id="bit"> <p>0</p> <p>1</p> </ref> <ref id="byte"> <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\ <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p> </ref> </grammar>
![]() |
首先,打开磁盘上的文件。这将为您提供一个 文件对象。 |
![]() |
将文件对象传递给 minidom.parse,它将调用 fsock 的 read 方法并从磁盘文件读取 XML 文档。 |
![]() |
请确保在使用完文件对象后调用其 close 方法。minidom.parse 不会为您执行此操作。 |
![]() |
对返回的 XML 文档调用 toxml() 方法将打印出整个文档。 |
好吧,这似乎是在浪费时间。毕竟,您已经看到 minidom.parse 可以简单地接收文件名并自动完成所有打开和关闭的繁琐操作。的确,如果您知道只需要解析本地文件,则可以传递文件名,minidom.parse 足够聪明,可以 做正确的事情™。但请注意,从 Internet 直接解析 XML 文档是多么相似——而且容易。
>>> import urllib >>> usock = urllib.urlopen('http://slashdot.org/slashdot.rdf')>>> xmldoc = minidom.parse(usock)
>>> usock.close()
>>> print xmldoc.toxml()
<?xml version="1.0" ?> <rdf:RDF xmlns="http://my.netscape.com/rdf/simple/0.9/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <channel> <title>Slashdot</title> <link>http://slashdot.org/</link> <description>News for nerds, stuff that matters</description> </channel> <image> <title>Slashdot</title> <url>http://images.slashdot.org/topics/topicslashdot.gif</url> <link>http://slashdot.org/</link> </image> <item> <title>To HDTV or Not to HDTV?</title> <link>http://slashdot.org/article.pl?sid=01/12/28/0421241</link> </item> [...snip...]
![]() |
正如您在 上一章 中看到的,urlopen 接收一个网页 URL 并返回一个类文件对象。最重要的是,此对象有一个 read 方法,该方法返回网页的 HTML 源代码。 |
![]() |
现在,将类文件对象传递给 minidom.parse,它将服从地调用对象的 read 方法并解析 read 方法返回的 XML 数据。这个 XML 数据现在直接来自网页这一事实完全无关紧要。minidom.parse 不知道网页,也不关心网页;它只知道类文件对象。 |
![]() |
使用完 urlopen 返回的类文件对象后,请务必将其关闭。 |
![]() |
顺便说一下,这个 URL 是真实的,而且确实是 XML。它是 Slashdot(一个技术新闻和八卦网站)上当前头条新闻的 XML 表示形式。 |
>>> contents = "<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> xmldoc = minidom.parseString(contents)>>> print xmldoc.toxml() <?xml version="1.0" ?> <grammar><ref id="bit"><p>0</p><p>1</p></ref></grammar>
好的,所以您可以使用 minidom.parse 函数来解析本地文件和远程 URL,但是对于解析字符串,您需要使用... 不同的函数。这意味着,如果您希望能够接收来自文件、URL 或字符串的输入,则需要特殊的逻辑来检查它是否是字符串,并改为调用 parseString 函数。多么不令人满意啊。
如果有一种方法可以将字符串转换为类文件对象,那么您就可以简单地将此对象传递给 minidom.parse。事实上,有一个模块专门用于执行此操作:StringIO。
>>> contents = "<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> import StringIO >>> ssock = StringIO.StringIO(contents)>>> ssock.read()
"<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> ssock.read()
'' >>> ssock.seek(0)
>>> ssock.read(15)
'<grammar><ref i' >>> ssock.read(15) "d='bit'><p>0</p" >>> ssock.read() '><p>1</p></ref></grammar>' >>> ssock.close()
>>> contents = "<grammar><ref id='bit'><p>0</p><p>1</p></ref></grammar>" >>> ssock = StringIO.StringIO(contents) >>> xmldoc = minidom.parse(ssock)>>> ssock.close() >>> print xmldoc.toxml() <?xml version="1.0" ?> <grammar><ref id="bit"><p>0</p><p>1</p></ref></grammar>
所以现在您知道了如何使用单个函数 minidom.parse 来解析存储在网页、本地文件或硬编码字符串中的 XML 文档。对于网页,使用 urlopen 获取类文件对象;对于本地文件,使用 open;对于字符串,使用 StringIO。现在让我们更进一步,概括 这些 差异。
def openAnything(source):# try to open with urllib (if source is http, ftp, or file URL) import urllib try: return urllib.urlopen(source)
except (IOError, OSError): pass # try to open with native open function (if source is pathname) try: return open(source)
except (IOError, OSError): pass # treat source as string import StringIO return StringIO.StringIO(str(source))
![]() |
openAnything 函数接收一个参数 source,并返回一个类文件对象。source 是某种字符串;它可以是 URL(例如 'http://slashdot.org/slashdot.rdf')、本地文件的完整或部分路径名(例如 'binary.xml'),或者包含要解析的实际 XML 数据的字符串。 |
![]() |
首先,您需要查看 source 是否是一个 URL。您可以通过暴力破解来实现:尝试将其作为 URL 打开,并静默忽略因尝试打开非 URL 对象而导致的错误。这实际上很优雅,因为如果 urllib 将来支持新的 URL 类型,您也可以在不重新编码的情况下支持它们。如果 urllib 能够打开 source,则 return 会立即将您踢出函数,并且以下 try 语句永远不会执行。 |
![]() |
另一方面,如果 urllib 对您发出警告并告诉您 source 不是有效的 URL,则您假设它是磁盘上文件的路径并尝试打开它。同样,您无需执行任何花哨的操作来检查 source 是否为有效文件名(无论如何,不同平台之间有效文件名的规则差异很大,因此您可能无论如何都会出错)。相反,您只需盲目地打开文件,并静默地捕获任何错误。 |
![]() |
至此,您需要假设 source 是一个包含硬编码数据的字符串(因为其他任何方法都行不通),因此您可以使用 StringIO 从中创建一个类似文件的对象并返回它。(实际上,由于您使用的是 str 函数,因此 source 甚至不需要是字符串;它可以是任何对象,您将使用其字符串表示形式,如其 __str__ 特殊方法 所定义。) |
现在,您可以将此 openAnything 函数与 minidom.parse 结合使用,以创建一个函数,该函数接受以某种方式引用 XML 文档的 source(作为 URL、本地文件名或字符串中的硬编码 XML 文档)并对其进行解析。
<< 继续 |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
标准输入、输出和错误 >> |