楼主: lastwinner

[参考文档] Python 研究(Dive Into Python)

[复制链接]
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
101#
 楼主| 发表于 2006-7-15 19:44 | 只看该作者
6.6. 全部放在一起
再一次,所有的多米诺骨牌都放好了。我们已经看过每行代码是如何工作的了。现在往回走一步,看一下放在一起是怎么样的。

例 6.21. listDirectory

def listDirectory(directory, fileExtList):                                         
    "get list of file info objects for files of particular extensions"
    fileList = [os.path.normcase(f)
                for f in os.listdir(directory)]           
    fileList = [os.path.join(directory, f)
               for f in fileList
                if os.path.splitext(f)[1] in fileExtList]                          
    def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):      
        "get file info class from filename extension"                             
        subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]        
        return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
    return [getFileInfoClass(f)(f) for f in fileList]                                listDirectory 是整个模块主要的引吸之处。它接收一个 dictionary (在我的例子中如 c:\music\_singles\) 和一个感兴趣的文件扩展名列表 (如 ['.mp3']),接着它返回一个类实例的 list ,这些类实例的行为象 dictionary,包含了在目录中每个感兴趣文件的元数据。并且实现起来只用了几行直观的代码。
  正如在 前一节 我们所看到的,这行代码得到一个全路径名的列表,它们是在 directory 中有着我们感兴趣的文件后缀 (由 fileExtList 所指定的) 的所有文件的路径名。
  老学校出身的 Pascal 程序员可能对嵌套函数感到熟悉,但大部分人,当我告诉他们 Python 支持嵌套函数时,都茫然地看着我。嵌套函数,从字面理解,是定义在函数内的函数。嵌套函数 getFileInfoClass 只能在定义它的函数 listDirectory 内进行调用。正如任何其它的函数一样,不需要一个接口声明或奇怪的什么东西,只要定义函数,开始编码就行了。
  既然你已经看过 os 模块了,这一行应该能理解了。它得到文件的扩展名 (os.path.splitext(filename)[1]),将其转换为大写字母 (.upper()),从圆点处进行分片 ([1:]),使用字符串格式化从其中生成一个类名。所以 c:\music\ap\mahadeva.mp3 变成 .mp3 再变成 MP3 再变成 MP3FileInfo。
  在生成完处理这个文件的处理类的名字之后,我们查阅在这个模块中是否存在这个处理类。如果存在,我们返回这个类,否则我们返回基类 FileInfo。这一点很重要: 这个函数返回一个类。不是类的实例,而是类本身。
  对每个属于我们 “感兴趣文件” 列表 (fileList)中的文件,我们用文件名 (f) 来调用 getFileInfoClass。调用 getFileInfoClass(f) 返回一个类;我们并不知道确切是哪一个类,但是我们并不关心。接着我们创建这个类 (不管它是什么) 的一个实例,传入文件名 (又是 f) 给的 __init__ 方法。正如我们在 本章的前面 所看到的,FileInfo 的 __init__ 方法设置了 self["name"],它将引发 __setitem__ 的调用,__setitem__ 在子类 (MP3FileInfo) 中被覆盖掉了,用来适当地对文件进行分析,取出文件的元数据。我们对所有感兴趣的文件进行处理,返回结果实例的一个 list。

请注意 listDirectory 完全是通用的。它事先不知道将得到文件哪种类型,或者哪些定义好的类能够处理这些文件。它检查目录中要进行处理的文件,然后反观本身模块,了解定义了什么特别的处理类 (象 MP3FileInfo)。你可以对这个程序进行扩充,对其它类型的文件进行处理,只要用适合的名字定义类:HTMLFileInfo 用于 HTML 文件,DOCFileInfo 用于 Word .doc 文件,等等。listDirectory 将会对它们都进行处理,不作改变,将工作交给适当的类,接着收集结果。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
102#
 楼主| 发表于 2006-7-15 19:44 | 只看该作者
6.7. 小结
在 第 5 章 介绍的 fileinfo.py 程序现在应该完全理解了。

"""Framework for getting filetype-specific metadata.

Instantiate appropriate class with filename.  Returned object acts like a
dictionary, with key-value pairs for each piece of metadata.
    import fileinfo
    info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3"
    print "\\n".join(["%s=%s" % (k, v) for k, v in info.items()])

Or use listDirectory function to get info on all files in a directory.
    for info in fileinfo.listDirectory("/music/ap/", [".mp3"]):
        ...

Framework can be extended by adding classes for particular file types, e.g.
HTMLFileInfo, MPGFileInfo, DOCFileInfo.  Each class is completely responsible for
parsing its files appropriately; see MP3FileInfo for example.
"""
import os
import sys
from UserDict import UserDict

def stripnulls(data):
    "strip whitespace and nulls"
    return data.replace("\00", "".strip()

class FileInfo(UserDict):
    "store file metadata"
    def __init__(self, filename=None):
        UserDict.__init__(self)
        self["name"] = filename

class MP3FileInfo(FileInfo):
    "store ID3v1.0 MP3 tags"
    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)}

    def __parse(self, filename):
        "parse ID3v1.0 tags from MP3 file"
        self.clear()
        try:                              
            fsock = open(filename, "rb", 0)
            try:                           
                fsock.seek(-128, 2)        
                tagdata = fsock.read(128)  
            finally:                       
                fsock.close()              
            if tagdata[:3] == "TAG":
                for tag, (start, end, parseFunc) in self.tagDataMap.items():
                    self[tag] = parseFunc(tagdata[start:end])               
        except IOError:                    
            pass                           

    def __setitem__(self, key, item):
        if key == "name" and item:
            self.__parse(item)
        FileInfo.__setitem__(self, key, item)

def listDirectory(directory, fileExtList):                                       
    "get list of file info objects for files of particular extensions"
    fileList = [os.path.normcase(f)
                for f in os.listdir(directory)]           
    fileList = [os.path.join(directory, f)
               for f in fileList
                if os.path.splitext(f)[1] in fileExtList]
    def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):      
        "get file info class from filename extension"                             
        subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]      
        return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
    return [getFileInfoClass(f)(f) for f in fileList]                             

if __name__ == "__main__":
    for info in listDirectory("/music/_singles/", [".mp3"]):
        print "\n".join(["%s=%s" % (k, v) for k, v in info.items()])
        print在研究下一章之前,确保你可以无困难地完成下面的事情:

使用 try...except 来捕捉异常
使用 try...finally 来保护额外的资源
读取 文件
在一个 for 循环 中一次赋多个值
使用 os 模块来满足你的跨平台文件操作的需要
动态地 实例化未知类型的类 通过将类看成对象并传入参数

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
103#
 楼主| 发表于 2006-7-15 19:45 | 只看该作者
第 7 章 正则表达式
7.1. 概览
7.2. 个案研究:街道地址
7.3. 个案研究:罗马字母
7.3.1. 校验千位数
7.3.2. 检验百位数
7.4. 使用{n,m} 语法
7.4.1. 校验十位数和个位数
7.5. 松散正则表达式
7.6. 个案研究: 解析电话号码
7.7. 小结
正则表达式是搜索、替换和解析复杂字符模式的一种强大而标准的方法。如果你曾经在其他语言(如Perl)中使用过它,他们的语法非常相似,那么你仅仅阅读一下re模块的摘要,大致了解其中可用的函数和参数就可以了。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
104#
 楼主| 发表于 2006-7-15 19:45 | 只看该作者
7.1. 概览
字符串也有很多方法,可以进行搜索(index, find, 和 count), 替换(replace)和解析 (split), 但他们仅限于处理最简单的情况。搜索方法查找单个和固定编码的子串,并且他们总是大小写敏感的。对一个字符串s, 如果要进行大小写不敏感的搜索,则你必须调用 s.lower() 或 s.upper() 将s转换成全小写或者全大写,然后确保搜索串有着相匹配的大小写。replace 和 split方法有着类似的限制。

如果你要解决的问题利用字符串函数能够完成,你应该使用他们。他们快速、简单且容易阅读,而对于快速、简单、可读性强的代码等方面有很多内容。但是,如果你发现你用了许多不同的字符串函数和 if语句来处理一个特殊情况,或者你组合使用了 split 、join 等函数而导致用一种奇怪的甚至读不下去的方式理解列表,此时,你也许需要转到正则表达式了。

尽管正则表达式语法较之普通代码相对麻烦一些,但是却可以得到更可读的结果,与用一长串字符串函数的解决方案相比要好很多。在正则表达式内部有多种方法嵌入注释,从而使之具有自文档化 (self-documenting) 的能力。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
105#
 楼主| 发表于 2006-7-15 19:45 | 只看该作者
7.2. 个案研究:街道地址
这一系列的例子是由我几年前日常工作中的现实问题启发而来的,当时我需要从一个老化系统中导出街道地址,在将他们导入新的系统之前,进行清理和标准化。(看,我不是只将这些东西堆到一起,他有实际的用处)。这个例子展示我如何处理这个问题。

例 7.1. 在字符串的结尾匹配
>>> s = '100 NORTH MAIN ROAD'
>>> s.replace('ROAD', 'RD.')               
'100 NORTH MAIN RD.'
>>> s = '100 NORTH BROAD ROAD'
>>> s.replace('ROAD', 'RD.')               
'100 NORTH BRD. RD.'
>>> s[:-4] + s[-4:].replace('ROAD', 'RD.')
'100 NORTH BROAD RD.'
>>> import re                              
>>> re.sub('ROAD$', 'RD.', s)               
'100 NORTH BROAD RD.'  我的目标是将街道地址标准化,'ROAD'通常被略写为'RD.'。乍看起来,我以为这个太简单了,只用字符串的方法replace就可以了。毕竟,所有的数据都已经是大写的了,因此大小写不匹配将不是问题。并且,要搜索的串'ROAD'是一个常量,在这个迷惑的简单例子中,s.replace的确能够胜任。
  不幸的是,生活充满了特例,并且我很快就意识到这个问题。比如:'ROAD'在地址中出现两次,一次是作为街道名称'BROAD'的一部分,一次是作为'ROAD'本身。replace方法遇到这两处的'ROAD'并没有区别,因此都进行了替换,而我发现地址被破坏掉了。
  为了解决在地址中出现多次'ROAD'子串的问题,有可能采用类似这样的方法:只在地址的最后四个字符中搜索替换'ROAD'(s[-4:]),忽略字符串的其他部分(s[:-4])。但是,你可能发现这已经变得不方便了。例如,该模式依赖于你要替换的字符串的长度了(如果你要把'STREET'替换为'ST.',你需要利用s[:-6]和s[-6:].replace(...))。你愿意在六月个期间回来调试他们么?我本人是不愿意的。
  是时候转到正则表达式了。在Python中,所有和正则表达式相关的功能都包含在re模块中。
  来看第一个参数:'ROAD$'。这个正则表达式非常简单,只有当'ROAD'出现在一个字符串的尾部时才会匹配。字符$表示“字符串的末尾”(还有一个对应的字符,尖号^,表示“字符串的开始”)。
  利用re.sub函数,对字符串s进行搜索,满足正则表达式'ROAD$'的用'RD.'替换。这样将匹配字符串s末尾的'ROAD',而不会匹配属于单词'ROAD'一部分的'ROAD',这是因为它是出现在s的中间。

继续我的清理地址的故事,很快,我发现上面的例子,仅仅匹配地址末尾的'ROAD'不是很好,因为不是所有的地址都包括一个街道的命名;有一些是以街道名结尾的。大部分情况下,不会遇到这种情况,但是,如果街道名称为'BROAD',那么正则表达式将会匹配'BROAD'的一部分为'ROAD',而这并不是我想要的。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
106#
 楼主| 发表于 2006-7-15 19:45 | 只看该作者
例 7.2. 匹配整个单词
>>> s = '100 BROAD'
>>> re.sub('ROAD$', 'RD.', s)
'100 BRD.'
>>> re.sub('\\bROAD$', 'RD.', s)  
'100 BROAD'
>>> re.sub(r'\bROAD$', 'RD.', s)  
'100 BROAD'
>>> s = '100 BROAD ROAD APT. 3'
>>> re.sub(r'\bROAD$', 'RD.', s)  
'100 BROAD ROAD APT. 3'
>>> re.sub(r'\bROAD\b', 'RD.', s)
'100 BROAD RD. APT 3'  我真正想要做的是,当'ROAD'出现在字符串的末尾,并且是作为一个独立的单词时,而不是一些长单词的一部分,才对他进行匹配。为了在正则表达式中表达这个意思,你利用\b,它的含义是“单词的边界必须在这里”。在Python中,由于字符'\'在一个字符串中必须转义这个事实,从而变得非常麻烦。有时候,这类问题被称为“反斜线灾难”,这也是Perl中正则表达式比Python的正则表达式要相对容易的原因之一。另一方面,Perl也混淆了正则表达式和其他语法,因此,如果你发现一个bug,很难弄清楚究竟是一个语法错误,还是一个正则表达式错误。
  为了避免反斜线灾难,你可以利用所谓的“原始字符串”,只要为字符串添加一个前缀 'r' 就可以了。这将告诉Python,字符串中的所有字符都不转义;'\t'是一个制表符,而r'\t'是一个真正的反斜线字符'\',紧跟着一个字母 't' 。我推荐只要处理正则表达式,就使用原始字符串;否则,事情会很快变得混乱(并且正则表达式自己也会很快被自己搞乱了)。
  (一声叹息),很不幸,我很快发现更多的与我的逻辑相矛盾的例子。在这个例子中,街道地址包含有作为整个单词的'ROAD',但是他不是在末尾,因为地址在街道命名后会有一个房间号。由于'ROAD'不是在每一个字符串的末尾,没有匹配上,因此调用re.sub没有替换任何东西,你获得的知识初始字符串,这也不是我们想要的。
  为了解决这个问题,我去掉了$字符,加上另一个\b。现在,正则表达式“匹配字符串中作为整个单词出现的'ROAD'了”,不论是在末尾、开始还是中间。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
107#
 楼主| 发表于 2006-7-15 19:45 | 只看该作者
7.3. 个案研究:罗马字母
7.3.1. 校验千位数
7.3.2. 检验百位数
你可能经常看到罗马数字,即使你没有意识到他们。你可能曾经在老电影或者电视中看到他们(“版权所有 MCMXLVI” 而不是 “版权所有1946”),或者在某图书馆或某大学的贡献墙上看到他们(“成立于 MDCCCLXXXVIII”而不是“成立于1888”)。你也可能在某些文献的大纲或者目录上看到他们。这是一个表示数字的系统,他能够真正回溯到远古的罗马帝国(因此而得名)。

在罗马数字中,利用7个不同字母进行重复或者组合来表达各式各样的数字。

I = 1
V = 5
X = 10
L = 50
C = 100
D = 500
M = 1000
下面是关于构造罗马数字的一些通用的规则的介绍:

字符是叠加的。 I表示1, II表示2, 而III表示3. VI 表示 6 (字面上为逐字符相加, “5 加 1”), VII 表示 7, VIII 表示 8.
能够被10整除的字符(I, X, C, 和 M)至多可以重复三次. 对于4, 你则需要利用下一个最大的能够被5整除的字符进行减操作得到,你不能把4 表示成 IIII; 而应表示为 IV (比“5小 1”)。数字40写成XL (比50小10), 41 写成 XLI, 42 写成 XLII, 43 写成 XLIII, 而 44 写成 XLIV (比50 小10, 然后比5小1).
类似的,对于数字 9,你必须利用下一个能够被10整除的字符进行减操作得到: 8 表示为 VIII, 而 9 则表示为 IX (比10 小1), 而不是 VIIII (因为字符I 不能连续重复四次)。数字90 表示为 XC, 900 表示为 CM.
被5整除的字符不能重复。数字10 常表示为X, 而从来不用VV来表示。数字100常表示为C, 也从来不表示为 LL.
罗马数字经常从高位到低位书写,从左到右阅读,因此不同顺序的字符意义大不相同。DC 表示 600; 而CD 是一个完全不同的数字(为400, 也就是比500 小100). CI 表示 101; 而IC 甚至不是一个合法的罗马字母(因为你不能直接从数字100减去1; 比需要写成XCIX, 意思是 比100 小10, 然后加上数字9,也就是比 10小1的数字).

本章译者注:“被5整除的数”这个译法并不严谨,因为所有被10整除的数也能够被5整除,此处表达的含义是:那些包含有5的含义的罗马数字字符。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
108#
 楼主| 发表于 2006-7-15 19:46 | 只看该作者
7.3.1. 校验千位数
怎样校验任意一个字符串是否为一个有效的罗马数字呢?我们每次只看一个数字,由于罗马数字经常是从高位到低位书写,我们从高位开始:千位。对于大于、等于1000的数字,千位有一系列的字符 M 表示。

例 7.3. 校验千位数
>>> import re
>>> pattern = '^M?M?M?$'      
>>> re.search(pattern, 'M')   
<SRE_Match object at 0106FB58>
>>> re.search(pattern, 'MM')   
<SRE_Match object at 0106C290>
>>> re.search(pattern, 'MMM')  
<SRE_Match object at 0106AA38>
>>> re.search(pattern, 'MMMM')
>>> re.search(pattern, '')     
<SRE_Match object at 0106F4A8>  这个模式有三部分:
^表示仅仅在一个字符串的开始匹配其后的字符串内容。如果没有这个字符,这个模式将匹配出现在字符串任意位置上的 M,而这并不是你想要的。你想确认的是:字符串中是否出现字符M,如果出现,则必须是在字符串的开始。
M? 可选的匹配单个字符M,由于他重复出现三次,你可以在一行中匹配0次到3次字符M。
$ 字符限制模式只能够在一个字符串的结尾匹配。当和模式开头的字符^结合使用时,这意味着模式必须匹配整个串,并且在在字符M的前后都不能够出现其他的任意字符。

  re 模块的本质是一个search 函数,该函数有两个参数,一个是正则表达式(pattern),一个是字符串 ('M'),函数试图匹配正则表达式。如果发现一个匹配,search 函数返回一个拥有多种方法可以描述这个匹配的对象,如果没有发现匹配,search 函数返回一个None, 一个Python 空值(null value)。你此刻关注的唯一事情,就是模式是否匹配上,可以利用 search函数的返回值弄清这个事实。字符串'M' 匹配上这个正则表达式,因为第一个可选的M匹配上,而第二个和第三个M 被忽略掉了。
  'MM' 匹配上是因为第一和第二个可选的M匹配上,而忽略掉第三个M。
  'MMM' 匹配上因为三个M 都匹配上了
  'MMMM' 没有匹配上。因为所有的三个M都匹配上,但是正则表达式还有字符串尾部的限制 (由于字符 $), 然而字符串没有结束(因为还有第四个M字符), 因此 search 函数返回一个None.
  有趣的是,一个空字符串也能够匹配这个正则表达式,因为所有的字符 M 都是可选的。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
109#
 楼主| 发表于 2006-7-15 19:46 | 只看该作者
7.3.2. 检验百位数
百位数的位置与千位数相比,识别起来要困难得多,这是因为有多种相互独立的表达方式都可以表达百位数,具体用那种方式表达和具体的数值相关。
  • 100 = C
  • 200 = CC
  • 300 = CCC
  • 400 = CD
  • 500 = D
  • 600 = DC
  • 700 = DCC
  • 800 = DCCC
  • 900 = CM

因此有四种可能的模式:
  • CM
  • CD
  • 零到三次出现C 字符 (如果是零,表示百位数为0)
  • D, 后面跟零个到三个C字符

后面两个模式可以结合到一起:
  • 一个可选的字符D, 加上零到3个C 字符。

这个例子显示如何有效的识别罗马数字的百位数位置。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
110#
 楼主| 发表于 2006-7-15 19:46 | 只看该作者
例 7.4. 检验百位数
>>> import re
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)$'
>>> re.search(pattern, 'MCM')            
<SRE_Match object at 01070390>
>>> re.search(pattern, 'MD')            
<SRE_Match object at 01073A50>
>>> re.search(pattern, 'MMMCCC')         
<SRE_Match object at 010748A8>
>>> re.search(pattern, 'MCMC')           
>>> re.search(pattern, '')               
<SRE_Match object at 01071D98>  这个模式的首部和上一个模式相同,检查字符串的开始(^), 接着匹配千位数位置(M?M?M?),然后才是这个模式新的内容,在括号内,定义了包含有三个互相独立的模式集合,由垂直线隔开:CM, CD, 和 D?C?C?C? (D是可选字符,接着是0到3个可选的C 字符)。正则表达式解析器依次检查这些模式(从左到右), 如果匹配上第一个模式,则忽略剩下的模式。
  'MCM' 匹配上,因为第一个M 字符匹配,第二和第三个M字符被忽略掉,而CM 匹配上 (因此 CD 和 D?C?C?C? 两个模式甚至不再考虑)。 MCM 表示罗马数字1900。
  'MD' 匹配上,因为第一个字符M 匹配上, 第二第三个M字符忽略,而模式D?C?C?C? 匹配上D (模式中的三个可选的字符C都被忽略掉了)。 MD 表示罗马数字1500。
  'MMMCCC' 匹配上,因为三个M 字符都匹配上,而模式D?C?C?C?匹配上CCC (字符D是可选的,此处忽略)。 MMMCCC 表示罗马数字3300。
  'MCMC' 没有匹配上。第一个M 字符匹配上,第二第三个M字符忽略,接着是CM 匹配上,但是接着是 $ 字符没有匹配,因为字符串还没有结束(你仍然还有一个没有匹配的C字符)。 C 字符 也不 匹配模式D?C?C?C?的一部分,因为与之相互独立的模式CM已经匹配上。
  有趣的是,一个空字符串也可以匹配这个模式,因为所有的 M 字符都是可选的,它们都被忽略,并且一个空字符串可以匹配D?C?C?C? 模式,此处所有的字符也都是可选的,并且都被忽略。

吆!来看正则表达式能够多快变得难以理解?你仅仅表示了罗马数字的千位和百位上的数字。如果你根据类似的方法,十位数和各位数就非常简单了,因为是完全相同的模式。让我们来看表达这个模式的另一种方式吧。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表