python 2.x和3.x中maketrans和translate函数的使用

本文的缘由在于今天在网上逛来逛去的时候,发现了一个Python解谜的网站http://projecteuler.net/www.pythonchallenge.com,鉴于自己Python一瓶不满的水平,直接草草做了一下,发现水的很,搞不出来就直接看答案了,主要题目是这个http://www.pythonchallenge.com/pc/def/map.html,具体的解答可以参考这篇文章http://garethrees.org/2007/05/07/python-challenge/
maketrans和translate函数是进行字符串字符编码的常用方法。本文着重点在于演示其基本用法和在不同版本下操作的差异。本文提到的2.X版本指2.6以上的版本,3.X版本指3.1以上的版本。
2.X版本把字符串基本分为两种:unicode字符串和8位字符串str,后者包含字节数据和我们常见的ASCII码数据;而3.X版本则重新对字符串进行了划分,分为了字节字符串bytes和文本字符串str,两者都是不可变的,所以添加了一个可变的字节字符串类型bytearray。
2.X版本中string类型和str、unicode类型大量方法是重复的,所以3.X版本不提倡使用string模块中与str重复的方法。string模块中还有很多有用的常量和方法,比如string.digits,可以在字符串编码中方便地使用。
2.X中maketrans和translate函数的签名:

    string.maketrans(from, to)  #Return a translation tablesuitable for use in string.translate.  The strings frm and to must be of the same length.
    string.translate(s, table[, deletechars])  #Return a copy of the string s, where all characters occurring in the optional argument deletions are removed, and the remaining characters have been mapped through the given translation table, which must be a string of length 256.  The deletions argument is not allowed for Unicode strings.
    str.translate(table[, deletechars])  
    unicode.translate(table)

2.X中maketrans和translate函数的签名:

    static str.maketrans(x[, y[, z]])  
    static bytes.maketrans(from, to)  
    static bytearray.maketrans(from, to)  
    str.translate(map)  
    bytes.translate(table[, delete])  
    bytearray.translate(table[, delete])  

从中可以看出,相对于2.X的string模块的maketrans方法,3.X中分别提供了三个静态方法用于创建映射表。下面让我们看一个简单的例子来说明字符串转换的过程:
2.X下的演示过程:

    >>> import string                       #导入string模块  
    >>> map = string.maketrans('123', 'abc')#建立映射表,将字符串中含有的'1','2','3'替换为'a','b','c'  
    >>> s = '54321123789'                   #转换前的字符串  
    >>> s.translate(map)                    #用创建的映射表map转换字符串  
    '54cbaabc789'                           #转换后的字符串

3.X下的演示过程:

    >>> map = str.maketrans('123','abc')  
    >>> s = '54321123789'  
    >>> s.translate(map)  
    '54cbaabc789'

2.X使用了string的maketrans函数,而3.X使用了str的maketrans函数,除了这一点,使用方法是基本相同的。若指定字符串中要删除的字符时,使用就会略有不同,如下:
2.X下的演示过程:

    >>> import string  
    >>> map = string.maketrans('123', 'abc')  
    >>> s = '54321123789'  
    >>> s.translate(map, '78')        #除了转换,还要删除字符串中的字符'7','8'  
    '54cbaabc9'                       #转换后的字符串没有字符'7','8'  

3.X下的演示过程:

    >>> map = str.maketrans('123','abc', '78')#要删除的字符需要在这指定  
    >>> s = '54321123789'  
    >>> s.translate(map)  
    '54cbaabc9'

在读《Python Cookbook》遇到了一个基于2.X版本的例子,如下

    import string   
    def translator(frm='', to='', delete='', keep=None):   
        if len(to) == 1:   
            to = to * len(frm)   
        trans = string.maketrans(frm, to)   
        if keep is not None:   
            allchars = string.maketrans('', '')   
            delete = allchars.translate(allchars, keep.translate(allchars,delete))  
        def translate(s):   
            return s.translate(trans, delete)   
        return translate  

见第8行代码,allchars应该是一个返回的映射表,为什么还可以调用translate方法,所以它应该是一个str类型,测试如下:

    >>> import string  
    >>> map = string.maketrans('123', 'abc')  
    >>> type(map)  
      

在3.X版本中这个方法不能正常通过运行,那么错在什么地方呢,我们看看映射表是什么类型:

    >>> map = str.maketrans('123','abc')  
    >>> type(map)  
      

知道了映射表的类型了,我们就可以对其进行“后期加工”,像上面《Python Cookbook》中的例子一样,来满足我们的编码要求。

上面讨论的例子用的字符串是ASCII字符组成的,如果是字节类型,2.X版本中操作是一样的,3.X中调用bytes或bytearray的函数;若是unicode类型的,2.X需要用unicode的translate方法,注意下面的代码


    >>> print u"hallo".translate({97:u'e'})  
    hello  
    >>> print u"hallo".translate({'a':u'e'})  
    hallo  
    >>> print u"hallo".translate({u'a':u'e'})  
    hallo  

结果之所以不一样,查阅手册可知unicode的translate方法的映射表也就是字典的键必须是unicode的位序数,值可以是unicode的位序数、unicode字符串或这None。
具体的相关信息,可以查阅Python的手册或者help(string)。