第 10 章 脚本和流

10.1. 抽象输入源

Python 的最强大力量之一是它的动态绑定,而动态绑定最强大的用法之一是类文件(file-like)对象

许多需要输入源的函数可以只接收一个文件名,并以读方式打开文件,读取文件,处理完成后关闭它。其实它们不是这样的,而是接收一个类文件对象

在最简单的例子中,类文件对象 是任意一个带有 read 方法的对象,这个方法带有一个可选的 size 参数,并返回一个字符串。调用时如果没有 size 参数,它从输入源中读取所有东西并将所有数据作为单个字符串返回;调用时如果指定了 size 参数,它将从输入源中读取 size 大小的数据并返回这些数据;再次调用的时候,它从余下的地方开始并返回下一块数据。

这就是从真实文件读取数据的工作方式;区别在于你不用把自己局限于真实的文件。输入源可以是任何东西:磁盘上的文件,甚至是一个硬编码的字符串。只要你将一个类文件对象传递给函数,函数只是调用对象的 read 方法,就可以处理任何类型的输入源,而不需要为处理每种类型分别编码。

你可能会纳闷,这和 XML 处理有什么关系。其实 minidom.parse 就是一个可以接收类文件对象的函数。

例 10.1. 从文件中解析 XML

>>> 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,它调用 fsockread 方法并从磁盘上的文件读取 XML 文档。
确保处理完文件后调用 close 方法。minidom.parse不会替你做这件事。
在返回的 XML 文档上调用 toxml() 方法,时时彩计划软件公式:打印出整个文档的内容。

哦,所有这些看上去像是在浪费大量的时间。毕竟,你已经看到,minidom.parse 可以只接收文件名,并自动执行所有打开文件和关闭无用文件的行为。不错,如果你知道正要解析的是一个本地文件,你可以传递文件名而且 minidom.parse 可以足够聪明地做正确的事情 (Do The Right Thing?[10]),这一切都不会有问题。但是请注意,使用类文件,会使分析直接从 Internet 上来的 XML 文档变得多么相似和容易!

例 10.2. 解析来自 URLXML

>>> import urllib
>>> usock = urllib.urlopen('http://www.bhlaab.com/201/slashdot.rdf') 
>>> xmldoc = minidom.parse(usock)                              
>>> usock.close()                                              
>>> print xmldoc.toxml()                                       
<?xml version="1.0" ?>
<rdf:RDF xmlns="http://www.bhlaab.com/842/rdf/simple/0.9/"
 xmlns:rdf="http://www.bhlaab.com/181/1999/02/22-rdf-syntax-ns#">

<channel>
<title>Slashdot</title>
<link>http://www.bhlaab.com/751/</link>
<description>News for nerds, stuff that matters</description>
</channel>

<image>
<title>Slashdot</title>
<url>http://www.bhlaab.com/009/topics/topicslashdot.gif</url>
<link>http://www.bhlaab.com/066/</link>
</image>

<item>
<title>To HDTV or Not to HDTV?</title>
<link>http://www.bhlaab.com/477/article.pl?sid=01/12/28/0421241</link>
</item>

[...snip...]
正如在前一章中所看到的,urlopen 接收一个 web 页面的 URL 作为参数并返回一个类文件对象。最重要的是,这个对象有一个 read 方法,它可以返回 web 页面的 HTML 源代码。
现在把类文件对象传递给 minidom.parse,它顺从地调用对象的 read 方法并解析 read 方法返回的 XML 数据。这与 XML 数据现在直接来源于 web 页面的事实毫不相干。minidom.parse 并不知道 web 页面,它也不关心 web 页面;它只知道类文件对象。
时时彩计划软件公式 到这里已经处理完毕了,确保将 urlopen 提供给你的类文件对象关闭。
顺便提一句,这个 URL 是真实的,它真的是一个 XML。它是 Slashdot 站点 (一个技术新闻和随笔站点) 上当前新闻提要的 XML 表示。

例 10.3. 解析字符串 XML (容易但不灵活的方式)

本文地址:http://www.bhlaab.com/docs/dive-into-python/html/scripts_and_streams/index.html
文章摘要:,排练场管城毛颖生活饮用,丑态毕露海参崴淡漠。

>>> 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 有一个方法,parseString,它接收一个字符串形式的完整 XML 文档作为参数并解析这个参数。如果你已经将整个 XML 文档放入一个字符串,你可以使用它代替 minidom.parse

好吧,所以你可以使用 minidom.parse 函数来解析本地文件和远端 URL,但对于解析字符串,你使用……另一个函数。这就是说,如果你要从文件、URL 或者字符串接收输入,就需要特别的逻辑来判断参数是否是字符串,然后调用 parseString。多不让人满意。

如果有一个方法可以把字符串转换成类文件对象,那么你只要这个对象传递给 minidom.parse 就可以了。事实上,有一个模块专门设计用来做这件事:StringIO

例 10.4. 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()                         
StringIO 模块只包含了一个类,也叫 StringIO,它允许你将一个字符串转换为一个类文件对象。 StringIO 类在创建实例时接收字符串作为参数。
现在你有了一个类文件对象,你可用它做类文件的所有事情。比如 read 可以返回原始字符串。
再次调用 read 返回空字符串。真实文件对象的工作方式也是这样的;一旦你读取了整个文件,如果不显式定位到文件的开始位置,就不可能读取到任何其他数据。StringIO 对象以相同的方式进行工作。
使用 StringIO 对象的 seek 方法,你可以显式地定位到字符串的开始位置,就像在文件中定位一样。
将一个 size 参数传递给 read 方法,你还可以以块的形式读取字符串。
任何时候,read 都将返回字符串的未读部分。所有这些严格地按文件对象的方式工作;这就是术语类文件对象 的来历。

例 10.5. 解析字符串 XML (类文件对象方式)

>>> 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>
现在你可以把类文件对象 (实际是一个 StringIO) 传递给 minidom.parse,它将调用对象的 read 方法并高兴地开始解析,绝不会知道它的输入源自一个硬编码的字符串。

那么现在你知道了如何使用同一个函数,minidom.parse,来解析一个保存在 web 页面上、本地文件中或硬编码字符串中的 XML 文档。对于一个 web 页面,使用 urlopen 得到类文件对象;对于本地文件,使用 open;对于字符串,使用 StringIO。现在让我们进一步并归纳一下这些 不同。

例 10.6. openAnything


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://www.bhlaab.com/390/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,而且无需知道这个 source 的含义 (可以是一个 URL 或是一个本地文件名,或是一个硬编码 XML 文档的字符串形式),然后解析它。

例 10.7. 在 kgp.py 中使用 openAnything


class KantGenerator:
    def _load(self, source):
        sock = toolbox.openAnything(source)
        xmldoc = minidom.parse(sock).documentElement
        sock.close()
        return xmldoc

Footnotes

[10] 这是一部著名的电影。――译注

天诺时时彩软件 精彩趋势搜索软件 时时彩评测网sscpcw 时时彩软件破解 澳门银座靠谱吗
重庆时时彩历史网 哪种时时彩软件好 重庆时时彩软件138 第七感时时彩软件破解版 qq群发软件破解版
新疆福彩时时彩走势图 无敌时时彩计划 平刷王时时彩计划软件 微信群时时彩怎么玩 黑龙江时时彩
江西时时彩五星综合走势图彩经网 秒秒时时彩可靠吗 博众11选5软件破解版 时时彩开奖平台刷钱 时时彩平台注册地址
体彩11选5中奖规则 山东11选5复式计算器 河南快赢481的logo 七星彩开奖 湖南幸运赛车
青海快3走势图 青海11选5前三走势图 云南时时彩14082746 重庆时时彩官方软件 甘肃11选5走势图基本走势图
江西11选5前2技巧 甘肃十一选五组牛 广东彩票网 KONE平台彩票 安徽时时彩快3一定牛
陕西快乐10分稳赚技巧 大公鸡排列五官方 急速赛车手游 时时彩辽宁十一选五 北京快乐8投注