Python Standard Library
翻译: Python 江湖群
2008-03-28 13:11:53
[index.html 返回首页]
1. 文件格式
1.1. 概览
本章将描述用于处理不同文件格式的模块.
1.1.1. Markup 语言
Python 提供了一些用于处理可扩展标记语言( Extensible Markup Language , XML ) 和超文本标记语言( Hypertext Markup Language , HTML )的扩展. Python 同样提供了对 标准通用标记语言( Standard Generalized Markup Language , SGML )的支持.
所有这些格式都有着相同的结构, 因为 HTML 和 XML 都来自 SGML . 每个文档都是由 起始标签( start tags ), 结束标签( end tags ), 文本(又叫字符数据), 以及实体引用( entity references )构成:
<document name="sample.xml"> <header>This is a header</header> <body>This is the body text. The text can contain plain text ("character data"), tags, and entities. </body> </document>
在这个例子中, <document>, <header>, 以及 <body> 是起始标签. 每个起始标签都有一个对应的结束标签, 使用斜线 "/" 标记. 起始标签可以包含多个属性, 比如这里的 name 属性.
起始标签和它对应的结束标签中的任何东西被称为 元素( element ). 这里 document 元素包含 header 和 body 两个元素.
" 是一个字符实体( character entity ). 字符实体用于在文本区域中表示特殊的保留字符, 使用 & 指示. 这里它代表一个引号, 常见字符实体还有 " < ( < )" 和 " > ( > )" .
虽然 XML , HTML , SGML 使用相同的结构块, 但它们还有一些不同点. 在 XML 中, 所有元素必须有起始和结束标签, 所有标签必须正确嵌套( well-formed ). 而且 XML 是区分大小写的, 所以 <document> 和 <Document> 是不同的元素类型.
HTML 有很高灵活性, HTML 语法分析器一般会自动补全缺失标签; 例如, 当遇到一个以 <P> 标签开始的新段落, 却没有对应结束标签, 语法分析器会自动添加一个 </P> 标签. HTML 也是区分大小写的. 另一方面, XML 允许你定义任何元素, 而 HTML 使用一些由 HTML 规范定义的固定元素.
SGML 有着更高的灵活性, 你可以使用自己的声明( declaration ) 定义源文件如何转换到元素结构, DTD ( document type description , 文件类型定义)可以用来 检查结构并补全缺失标签. 技术上来说, HTML 和 XML 都是 SGML 应用, 有各自的 SGML 声明, 而且 HTML 有一个标准 DTD .
Python 提供了多个 makeup 语言分析器. 由于 SGML 是最灵活的格式, Python 的 sgmllib 事实上很简单. 它不会去处理 DTD , 不过你可以继承它来提供更复杂的功能.
Python 的 HTML 支持基于 SGML 分析器. htmllib 将具体的格式输出工作交给 formatter 对象. formatter 模块包含一些标准格式化标志.
Python 的 XML 支持模块很复杂. 先前是只有与 sgmllib 类似的 xmllib , 后来加入了更高级的
expat 模块(可选). 而最新版本中已经准备废弃 xmllib ,启用 xml 包作为工具集.
1.1.2. 配置文件
ConfigParser 模块用于读取简单的配置文件, 类似 Windows 下的 INI 文件.
netrc 模块用于读取 .netrc 配置文件, shlex 模块用于读取类似 shell 脚本语法的配置文件.
1.1.3. 压缩档案格式
Python 的标准库提供了对 GZIP 和 ZIP ( 2.0 及以后) 格式的支持. 基于 zlib 模块, gzip 和 zipfile 模块分别用来处理这类文件.
1.2. xmllib 模块
xmllib 已在当前版本中申明不支持.
xmlib 模块提供了一个简单的 XML 语法分析器, 使用正则表达式将 XML 数据分离, 如 [#eg-5-1 Example 5-1] 所示. 语法分析器只对文档做基本的检查, 例如是否只有一个顶层元素, 所有的标签是否匹配.
XML 数据一块一块地发送给 xmllib 分析器(例如在网路中传输的数据). 分析器在遇到起始标签, 数据区域, 结束标签, 和实体的时候调用不同的方法.
如果你只是对某些标签感兴趣, 你可以定义特殊的 start_tag 和 end_tag 方法, 这里 tag 是标签名称. 这些 start 函数使用它们对应标签的属性作为参数调用(传递时为一个字典).
1.2.0.1. Example 5-1. 使用 xmllib 模块获取元素的信息
File: xmllib-example-1.py import xmllib class Parser(xmllib.XMLParser): # get quotation number def _ _init_ _(self, file=None): xmllib.XMLParser._ _init_ _(self) if file: self.load(file) def load(self, file): while 1: s = file.read(512) if not s: break self.feed(s) self.close() def start_quotation(self, attrs): print "id =>", attrs.get("id") raise EOFError try: c = Parser() c.load(open("samples/sample.xml")) except EOFError: pass *B*id => 031*b*
[#eg-5-2 Example 5-2] 展示了一个简单(不完整)的内容输出引擎( rendering engine ). 分析器有一个元素堆栈( _ _tags ), 它连同文本片断传递给输出生成器. 生成器会在 style 字典中查询当前标签的层次, 如果不存在, 它将根据样式表创建一个新的样式描述.
1.2.0.2. Example 5-2. 使用 xmllib 模块
File: xmllib-example-2.py import xmllib import string, sys STYLESHEET = { # each element can contribute one or more style elements "quotation": {"style": "italic"}, "lang": {"weight": "bold"}, "name": {"weight": "medium"}, } class Parser(xmllib.XMLParser): # a simple styling engine def _ _init_ _(self, renderer): xmllib.XMLParser._ _init_ _(self) self._ _data = [] self._ _tags = [] self._ _renderer = renderer def load(self, file): while 1: s = file.read(8192) if not s: break self.feed(s) self.close() def handle_data(self, data): self._ _data.append(data) def unknown_starttag(self, tag, attrs): if self._ _data: text = string.join(self._ _data, "") self._ _renderer.text(self._ _tags, text) self._ _tags.append(tag) self._ _data = [] def unknown_endtag(self, tag): self._ _tags.pop() if self._ _data: text = string.join(self._ _data, "") self._ _renderer.text(self._ _tags, text) self._ _data = [] class DumbRenderer: def _ _init_ _(self): self.cache = {} def text(self, tags, text): # render text in the style given by the tag stack tags = tuple(tags) style = self.cache.get(tags) if style is None: # figure out a combined style style = {} for tag in tags: s = STYLESHEET.get(tag) if s: style.update(s) self.cache[tags] = style # update cache # write to standard output sys.stdout.write("%s =>\n" % style) sys.stdout.write(" " + repr(text) + "\n") # # try it out r = DumbRenderer() c = Parser(r) c.load(open("samples/sample.xml")) *B*{'style': 'italic'} => 'I\'ve had a lot of developers come up to me and\012say, "I haven\'t had this much fun in a long time. It sure beats\012writing ' {'style': 'italic', 'weight': 'bold'} => 'Cobol' {'style': 'italic'} => '" -- ' {'style': 'italic', 'weight': 'medium'} => 'James Gosling' {'style': 'italic'} => ', on\012' {'weight': 'bold'} => 'Java' {'style': 'italic'} => '.'*b*
1.3. xml.parsers.expat 模块
(可选) xml.parsers.expat 模块是 James Clark's Expat XML parser 的接口. [#eg-5-3 Example 5-3] 展示了这个功能完整且性能很好的语法分析器.
1.3.0.1. Example 5-3. 使用 xml.parsers.expat 模块
File: xml-parsers-expat-example-1.py from xml.parsers import expat class Parser: def _ _init_ _(self): self._parser = expat.ParserCreate() self._parser.StartElementHandler = self.start self._parser.EndElementHandler = self.end self._parser.CharacterDataHandler = self.data def feed(self, data): self._parser.Parse(data, 0) def close(self): self._parser.Parse("", 1) # end of data del self._parser # get rid of circular references def start(self, tag, attrs): print "START", repr(tag), attrs def end(self, tag): print "END", repr(tag) def data(self, data): print "DATA", repr(data) p = Parser() p.feed("<tag>data</tag>") p.close() *B*START u'tag' {} DATA u'data' END u'tag'*b*
注意即使你传入的是普通的文本, 这里的分析器仍然会返回 Unicode 字符串. 默认情况下, 分析器将源文本作为 UTF-8 解析. 如果要使用其他编码, 请确保 XML 文件包含 encoding 说明. 如 [#eg-5-4 Example 5-4] 所示.
1.3.0.2. Example 5-4. 使用 xml.parsers.expat 模块读取 ISO Latin-1 文本
File: xml-parsers-expat-example-2.py from xml.parsers import expat class Parser: def _ _init_ _(self): self._parser = expat.ParserCreate() self._parser.StartElementHandler = self.start self._parser.EndElementHandler = self.end self._parser.CharacterDataHandler = self.data def feed(self, data): self._parser.Parse(data, 0) def close(self): self._parser.Parse("", 1) # end of data del self._parser # get rid of circular references def start(self, tag, attrs): print "START", repr(tag), attrs def end(self, tag): print "END", repr(tag) def data(self, data): print "DATA", repr(data) p = Parser() p.feed("""\ <?xml version='1.0' encoding='iso-8859-1'?> <author> <name>fredrik lundh</name> <city>linköping</city> </author> """ ) p.close() *B*START u'author' {} DATA u'\012' START u'name' {} DATA u'fredrik lundh' END u'name' DATA u'\012' START u'city' {} DATA u'link\366ping' END u'city' DATA u'\012' END u'author'*b*
1.4. sgmllib 模块
sgmllib 模块, 提供了一个基本的 SGML 语法分析器. 它与 xmllib 分析器基本相同, 但限制更少(而且不是很完善). 如 [#eg-5-5 Example 5-5] 所示.
和在 xmllib 中一样, 这个分析器在遇到起始标签, 数据区域, 结束标签以及实体时调用内部方法. 如果你只是对某些标签感兴趣, 那么你可以定义特殊的方法.
1.4.0.1. Example 5-5. 使用 sgmllib 模块提取 Title 元素
File: sgmllib-example-1.py import sgmllib import string class FoundTitle(Exception): pass class ExtractTitle(sgmllib.SGMLParser): def _ _init_ _(self, verbose=0): sgmllib.SGMLParser._ _init_ _(self, verbose) self.title = self.data = None def handle_data(self, data): if self.data is not None: self.data.append(data) def start_title(self, attrs): self.data = [] def end_title(self): self.title = string.join(self.data, "") raise FoundTitle # abort parsing! def extract(file): # extract title from an HTML/SGML stream p = ExtractTitle() try: while 1: # read small chunks s = file.read(512) if not s: break p.feed(s) p.close() except FoundTitle: return p.title return None # # try it out print "html", "=>", extract(open("samples/sample.htm")) print "sgml", "=>", extract(open("samples/sample.sgm")) html => A Title. sgml => Quotations
重载 unknown_starttag 和 unknown_endtag 方法就可以处理所有的标签. 如 [#eg-5-6 Example 5-6] 所示.
1.4.0.2. Example 5-6. 使用 sgmllib 模块格式化 SGML 文档
File: sgmllib-example-2.py import sgmllib import cgi, sys class PrettyPrinter(sgmllib.SGMLParser): # A simple SGML pretty printer def _ _init_ _(self): # initialize base class sgmllib.SGMLParser._ _init_ _(self) self.flag = 0 def newline(self): # force newline, if necessary if self.flag: sys.stdout.write("\n") self.flag = 0 def unknown_starttag(self, tag, attrs): # called for each start tag # the attrs argument is a list of (attr, value) # tuples. convert it to a string. text = "" for attr, value in attrs: text = text + " %s='%s'" % (attr, cgi.escape(value)) self.newline() sys.stdout.write("<%s%s>\n" % (tag, text)) def handle_data(self, text): # called for each text section sys.stdout.write(text) self.flag = (text[-1:] != "\n") def handle_entityref(self, text): # called for each entity sys.stdout.write("&%s;" % text) def unknown_endtag(self, tag): # called for each end tag self.newline() sys.stdout.write("<%s>" % tag) # # try it out file = open("samples/sample.sgm") p = PrettyPrinter() p.feed(file.read()) p.close() *B*<chapter> <title> Quotations <title> <epigraph> <attribution> eff-bot, June 1997 <attribution> <para> <quote> Nobody expects the Spanish Inquisition! Amongst our weaponry are such diverse elements as fear, surprise, ruthless efficiency, and an almost fanatical devotion to Guido, and nice red uniforms — oh, damn! <quote> <para> <epigraph> <chapter>*b*
[#eg-5-7 Example 5-7] 检查 SGML 文档是否是如 XML 那样 "正确格式化", 所有的元素是否正确嵌套, 起始和结束标签是否匹配等.
我们使用列表保存所有起始标签, 然后检查每个结束标签是否匹配前个起始标签. 最后确认到达文件末尾时没有未关闭的标签.
1.4.0.3. Example 5-7. 使用 sgmllib 模块检查格式
File: sgmllib-example-3.py import sgmllib class WellFormednessChecker(sgmllib.SGMLParser): # check that an SGML document is 'well-formed' # (in the XML sense). def _ _init_ _(self, file=None): sgmllib.SGMLParser._ _init_ _(self) self.tags = [] if file: self.load(file) def load(self, file): while 1: s = file.read(8192) if not s: break self.feed(s) self.close() def close(self): sgmllib.SGMLParser.close(self) if self.tags: raise SyntaxError, "start tag %s not closed" % self.tags[-1] def unknown_starttag(self, start, attrs): self.tags.append(start) def unknown_endtag(self, end): start = self.tags.pop() if end != start: raise SyntaxError, "end tag %s does't match start tag %s" %\ (end, start) try: c = WellFormednessChecker() c.load(open("samples/sample.htm")) except SyntaxError: raise # report error else: print "document is well-formed" *B*Traceback (innermost last): ... SyntaxError: end tag head does't match start tag meta*b*
最后, [#eg-5-8 Example 5-8] 中的类可以用来过滤 HTML 和 SGML 文档. 继承这个类, 然后实现 start 和 end 方法即可.
1.4.0.4. Example 5-8. 使用 sgmllib 模块过滤 SGML 文档
File: sgmllib-example-4.py import sgmllib import cgi, string, sys class SGMLFilter(sgmllib.SGMLParser): # sgml filter. override start/end to manipulate # document elements def _ _init_ _(self, outfile=None, infile=None): sgmllib.SGMLParser._ _init_ _(self) if not outfile: outfile = sys.stdout self.write = outfile.write if infile: self.load(infile) def load(self, file): while 1: s = file.read(8192) if not s: break self.feed(s) self.close() def handle_entityref(self, name): self.write("&%s;" % name) def handle_data(self, data): self.write(cgi.escape(data)) def unknown_starttag(self, tag, attrs): tag, attrs = self.start(tag, attrs) if tag: if not attrs: self.write("<%s>" % tag) else: self.write("<%s" % tag) for k, v in attrs: self.write(" %s=%s" % (k, repr(v))) self.write(">") def unknown_endtag(self, tag): tag = self.end(tag) if tag: self.write("</%s>" % tag) def start(self, tag, attrs): return tag, attrs # override def end(self, tag): return tag # override class Filter(SGMLFilter): def fixtag(self, tag): if tag == "em": tag = "i" if tag == "string": tag = "b" return string.upper(tag) def start(self, tag, attrs): return self.fixtag(tag), attrs def end(self, tag): return self.fixtag(tag) c = Filter() c.load(open("samples/sample.htm"))
1.5. htmllib 模块
htmlib 模块包含了一个标签驱动的( tag-driven ) HTML 语法分析器, 它会将数据发送至一个格式化对象. 如 [#eg-5-9 Example 5-9] 所示. 更多关于如何解析 HTML 的例子请参阅 formatter 模块.
1.5.0.1. Example 5-9. 使用 htmllib 模块
File: htmllib-example-1.py import htmllib import formatter import string class Parser(htmllib.HTMLParser): # return a dictionary mapping anchor texts to lists # of associated hyperlinks def _ _init_ _(self, verbose=0): self.anchors = {} f = formatter.NullFormatter() htmllib.HTMLParser._ _init_ _(self, f, verbose) def anchor_bgn(self, href, name, type): self.save_bgn() self.anchor = href def anchor_end(self): text = string.strip(self.save_end()) if self.anchor and text: self.anchors[text] = self.anchors.get(text, []) + [self.anchor] file = open("samples/sample.htm") html = file.read() file.close() p = Parser() p.feed(html) p.close() for k, v in p.anchors.items(): print k, "=>", v print *B*link => ['http://www.python.org']*b*
如果你只是想解析一个 HTML 文件, 而不是将它交给输出设备, 那么 sgmllib 模块会是更好的选择.
1.6. htmlentitydefs 模块
htmlentitydefs 模块包含一个由 HTML 中 ISO Latin-1 字符实体构成的字典. 如 [#eg-5-10 Example 5-10] 所示.
1.6.0.1. Example 5-10. 使用 htmlentitydefs 模块
File: htmlentitydefs-example-1.py import htmlentitydefs entities = htmlentitydefs.entitydefs for entity in "amp", "quot", "copy", "yen": print entity, "=", entities[entity] *B*amp = & quot = " copy = \302\251 yen = \302\245*b*
Example 5-11 展示了如何将正则表达式与这个字典结合起来翻译字符串中的实体 ( cgi.escape 的逆向操作).
1.6.0.2. Example 5-11. 使用 htmlentitydefs 模块翻译实体
File: htmlentitydefs-example-2.py import htmlentitydefs import re import cgi pattern = re.compile("&(\w+?);") def descape_entity(m, defs=htmlentitydefs.entitydefs): # callback: translate one entity to its ISO Latin value try: return defs[m.group(1)] except KeyError: return m.group(0) # use as is def descape(string): return pattern.sub(descape_entity, string) print descape("<spam&eggs>") print descape(cgi.escape("<spam&eggs>")) *B*<spam&eggs> <spam&eggs>*b*
最后, [#eg-5-12 Example 5-12] 展示了如何将 XML 保留字符和 ISO Latin-1 字符转换为 XML 字符串. 与 cgi.escape 相似, 但它会替换非 ASCII 字符.
1.6.0.3. Example 5-12. 转义 ISO Latin-1 实体
File: htmlentitydefs-example-3.py import htmlentitydefs import re, string # this pattern matches substrings of reserved and non-ASCII characters pattern = re.compile(r"[&<>\"\x80-\xff]+") # create character map entity_map = {} for i in range(256): entity_map[chr(i)] = "&%d;" % i for entity, char in htmlentitydefs.entitydefs.items(): if entity_map.has_key(char): entity_map[char] = "&%s;" % entity def escape_entity(m, get=entity_map.get): return string.join(map(get, m.group()), "") def escape(string): return pattern.sub(escape_entity, string) print escape("<spam&eggs>") print escape("\303\245 i \303\245a \303\244 e \303\266") *B*<spam&eggs> å i åa ä e ö*b*
1.7. formatter 模块
formatter 模块提供了一些可用于 htmllib 的格式类( formatter classes ).
这些类有两种, formatter 和 writer . formatter 将 HTML 解析器的标签和数据流转换为适合输出设备的事件流( event stream ), 而 writer 将事件流输出到设备上. 如 [#eg-5-13 Example 5-13] 所示.
大多情况下, 你可以使用 AbstractFormatter 类进行格式化. 它会根据不同的格式化事件调用 writer 对象的方法. AbstractWriter 类在每次方法调用时打印一条信息.
1.7.0.1. Example 5-13. 使用 formatter 模块将 HTML 转换为事件流
File: formatter-example-1.py import formatter import htmllib w = formatter.AbstractWriter() f = formatter.AbstractFormatter(w) file = open("samples/sample.htm") p = htmllib.HTMLParser(f) p.feed(file.read()) p.close() file.close() *B*send_paragraph(1) new_font(('h1', 0, 1, 0)) send_flowing_data('A Chapter.') send_line_break() send_paragraph(1) new_font(None) send_flowing_data('Some text. Some more text. Some') send_flowing_data(' ') new_font((None, 1, None, None)) send_flowing_data('emphasized') new_font(None) send_flowing_data(' text. A') send_flowing_data(' link') send_flowing_data('[1]') send_flowing_data('.')*b*
formatter 模块还提供了 NullWriter 类, 它会将任何传递给它的事件忽略; 以及 DumbWriter 类, 它会将事件流转换为纯文本文档. 如 [#eg-5-14 Example 5-14] 所示.
1.7.0.2. Example 5-14. 使用 formatter 模块将 HTML 转换为纯文本
File: formatter-example-2.py import formatter import htmllib w = formatter.DumbWriter() # plain text f = formatter.AbstractFormatter(w) file = open("samples/sample.htm") # print html body as plain text p = htmllib.HTMLParser(f) p.feed(file.read()) p.close() file.close() # print links print print i = 1 for link in p.anchorlist: print i, "=>", link i = i + 1 *B*A Chapter. Some text. Some more text. Some emphasized text. A link[1]. 1 => http://www.python.org*b*
[#eg-5-15 Example 5-15] 提供了一个自定义的 Writer , 它继承自 DumbWriter 类, 会记录当前字体样式并根据字体美化输出格式.
1.7.0.3. Example 5-15. 使用 formatter 模块自定义 Writer
File: formatter-example-3.py import formatter import htmllib, string class Writer(formatter.DumbWriter): def _ _init_ _(self): formatter.DumbWriter._ _init_ _(self) self.tag = "" self.bold = self.italic = 0 self.fonts = [] def new_font(self, font): if font is None: font = self.fonts.pop() self.tag, self.bold, self.italic = font else: self.fonts.append((self.tag, self.bold, self.italic)) tag, bold, italic, typewriter = font if tag is not None: self.tag = tag if bold is not None: self.bold = bold if italic is not None: self.italic = italic def send_flowing_data(self, data): if not data: return atbreak = self.atbreak or data[0] in string.whitespace for word in string.split(data): if atbreak: self.file.write(" ") if self.tag in ("h1", "h2", "h3"): word = string.upper(word) if self.bold: word = "*" + word + "*" if self.italic: word = "_" + word + "_" self.file.write(word) atbreak = 1 self.atbreak = data[-1] in string.whitespace w = Writer() f = formatter.AbstractFormatter(w) file = open("samples/sample.htm") # print html body as plain text p = htmllib.HTMLParser(f) p.feed(file.read()) p.close() *B*_A_ _CHAPTER._ Some text. Some more text. Some *emphasized* text. A link[1].*b*
1.8. ConfigParser 模块
ConfigParser 模块用于读取配置文件.
配置文件的格式与 Windows INI 文件类似, 可以包含一个或多个区域( section ), 每个区域可以有多个配置条目.
这里有个样例配置文件, 在 [#eg-5-16 Example 5-16] 用到了这个文件:
[book] title: The Python Standard Library author: Fredrik Lundh email: [email protected] version: 2.0-001115 [ematter] pages: 250 [hardcopy] pages: 350
[#eg-5-16 Example 5-16] 使用 ConfigParser 模块读取这个配制文件.
1.8.0.1. Example 5-16. 使用 ConfigParser 模块
File: configparser-example-1.py import ConfigParser import string config = ConfigParser.ConfigParser() config.read("samples/sample.ini") # print summary print print string.upper(config.get("book", "title")) print "by", config.get("book", "author"), print "(" + config.get("book", "email") + ")" print print config.get("ematter", "pages"), "pages" print # dump entire config file for section in config.sections(): print section for option in config.options(section): print " ", option, "=", config.get(section, option) *B*THE PYTHON STANDARD LIBRARY by Fredrik Lundh ([email protected]) 250 pages book title = The Python Standard Library email = [email protected] author = Fredrik Lundh version = 2.0-001115 _ _name_ _ = book ematter _ _name_ _ = ematter pages = 250 hardcopy _ _name_ _ = hardcopy pages = 350*b*
Python 2.0 以后, ConfigParser 模块也可以将配置数据写入文件, 如 [#eg-5-17 Example 5-17] 所示.
1.8.0.2. Example 5-17. 使用 ConfigParser 模块写入配置数据
File: configparser-example-2.py import ConfigParser import sys config = ConfigParser.ConfigParser() # set a number of parameters config.add_section("book") config.set("book", "title", "the python standard library") config.set("book", "author", "fredrik lundh") config.add_section("ematter") config.set("ematter", "pages", 250) # write to screen config.write(sys.stdout) *B*[book] title = the python standard library author = fredrik lundh [ematter] pages = 250*b*
1.9. netrc 模块
netrc 模块可以用来解析 .netrc 配置文件, 如 Example 5-18 所示. 该文件用于在用户的 home 目录储存 FTP 用户名和密码. (别忘记设置这个文件的属性为: "chmod 0600 ~/.netrc," 这样只有当前用户能访问).
1.9.0.1. Example 5-18. 使用 netrc 模块
File: netrc-example-1.py import netrc # default is $HOME/.netrc info = netrc.netrc("samples/sample.netrc") login, account, password = info.authenticators("secret.fbi") print "login", "=>", repr(login) print "account", "=>", repr(account) print "password", "=>", repr(password) *B*login => 'mulder' account => None password => 'trustno1'*b*
1.10. shlex 模块
shlex 模块为基于 Unix shell 语法的语言提供了一个简单的 lexer (也就是 tokenizer). 如 [#eg-5-19 Example 5-19] 所示.
1.10.0.1. Example 5-19. 使用 shlex 模块
File: shlex-example-1.py import shlex lexer = shlex.shlex(open("samples/sample.netrc", "r")) lexer.wordchars = lexer.wordchars + "._" while 1: token = lexer.get_token() if not token: break print repr(token) *B*'machine' 'secret.fbi' 'login' 'mulder' 'password' 'trustno1' 'machine' 'non.secret.fbi' 'login' 'scully' 'password' 'noway'*b*
1.11. zipfile 模块
( 2.0 新增) zipfile 模块可以用来读写 ZIP 格式.
1.11.1. 列出内容
使用 namelist 和 infolist 方法可以列出压缩档的内容, 前者返回由文件名组成的列表, 后者返回由 ZipInfo 实例组成的列表. 如 [#eg-5-20 Example 5-20] 所示.
1.11.1.1. Example 5-20. 使用 zipfile 模块列出 ZIP 文档中的文件
File: zipfile-example-1.py import zipfile file = zipfile.ZipFile("samples/sample.zip", "r") # list filenames for name in file.namelist(): print name, print # list file information for info in file.infolist(): print info.filename, info.date_time, info.file_size *B*sample.txt sample.jpg sample.txt (1999, 9, 11, 20, 11, 8) 302 sample.jpg (1999, 9, 18, 16, 9, 44) 4762*b*
1.11.2. 从 ZIP 文件中读取数据
调用 read 方法就可以从 ZIP 文档中读取数据. 它接受一个文件名作为参数, 返回字符串. 如 [#eg-5-21 Example 5-21] 所示.
1.11.2.1. Example 5-21. 使用 zipfile 模块从 ZIP 文件中读取数据
File: zipfile-example-2.py import zipfile file = zipfile.ZipFile("samples/sample.zip", "r") for name in file.namelist(): data = file.read(name) print name, len(data), repr(data[:10]) *B*sample.txt 302 'We will pe' sample.jpg 4762 '\377\330\377\340\000\020JFIF'*b*
1.11.3. 向 ZIP 文件写入数据
向压缩档加入文件很简单, 将文件名, 文件在 ZIP 档中的名称传递给 write 方法即可.
[#eg-5-22 Example 5-22] 将 samples 目录中的所有文件打包为一个 ZIP 文件.
1.11.3.1. Example 5-22. 使用 zipfile 模块将文件储存在 ZIP 文件里
File: zipfile-example-3.py import zipfile import glob, os # open the zip file for writing, and write stuff to it file = zipfile.ZipFile("test.zip", "w") for name in glob.glob("samples/*"): file.write(name, os.path.basename(name), zipfile.ZIP_DEFLATED) file.close() # open the file again, to see what's in it file = zipfile.ZipFile("test.zip", "r") for info in file.infolist(): print info.filename, info.date_time, info.file_size, info.compress_size *B*sample.wav (1999, 8, 15, 21, 26, 46) 13260 10985 sample.jpg (1999, 9, 18, 16, 9, 44) 4762 4626 sample.au (1999, 7, 18, 20, 57, 34) 1676 1103 ...*b*
write 方法的第三个可选参数用于控制是否使用压缩. 默认为 zipfile.ZIP_STORED , 意味着只是将数据储存在档案里而不进行任何压缩. 如果安装了 zlib 模块, 那么就可以使用 zipfile.ZIP_DEFLATED 进行压缩.
zipfile 模块也可以向档案中添加字符串. 不过, 这需要一点技巧, 你需要创建一个 ZipInfo 实例, 并正确配置它. [#eg-5-23 Example 5-23] 提供了一种简单的解决办法.
1.11.3.2. Example 5-23. 使用 zipfile 模块在 ZIP 文件中储存字符串
File: zipfile-example-4.py import zipfile import glob, os, time file = zipfile.ZipFile("test.zip", "w") now = time.localtime(time.time())[:6] for name in ("life", "of", "brian"): info = zipfile.ZipInfo(name) info.date_time = now info.compress_type = zipfile.ZIP_DEFLATED file.writestr(info, name*1000) file.close() # open the file again, to see what's in it file = zipfile.ZipFile("test.zip", "r") for info in file.infolist(): print info.filename, info.date_time, info.file_size, info.compress_size *B*life (2000, 12, 1, 0, 12, 1) 4000 26 of (2000, 12, 1, 0, 12, 1) 2000 18 brian (2000, 12, 1, 0, 12, 1) 5000 31*b*
1.12. gzip 模块
gzip 模块用来读写 gzip 格式的压缩文件, 如 [#eg-5-24 Example 5-24] 所示.
1.12.0.1. Example 5-24. 使用 gzip 模块读取压缩文件
File: gzip-example-1.py import gzip file = gzip.GzipFile("samples/sample.gz") print file.read() *B*Well it certainly looks as though we're in for a splendid afternoon's sport in this the 127th Upperclass Twit of the Year Show.*b*
标准的实现并不支持 seek 和 tell 方法. 不过 [#eg-5-25 Example 5-25] 可以解决这个问题.
1.12.0.2. Example 5-25. 给 gzip 模块添加 seek/tell 支持
File: gzip-example-2.py import gzip class gzipFile(gzip.GzipFile): # adds seek/tell support to GzipFile offset = 0 def read(self, size=None): data = gzip.GzipFile.read(self, size) self.offset = self.offset + len(data) return data def seek(self, offset, whence=0): # figure out new position (we can only seek forwards) if whence == 0: position = offset elif whence == 1: position = self.offset + offset else: raise IOError, "Illegal argument" if position < self.offset: raise IOError, "Cannot seek backwards" # skip forward, in 16k blocks while position > self.offset: if not self.read(min(position - self.offset, 16384)): break def tell(self): return self.offset # # try it file = gzipFile("samples/sample.gz") file.seek(80) print file.read() *B*this the 127th Upperclass Twit of the Year Show.*b*