Welcome, guest | Sign In | My Account | Store | Cart

It seems like every time I want to use string.maketrans and str.translate, I have to stop for a minute and think really hard about how to accomplish what I want to do. Eventually, I wrote this little wrapper class to ease my pain.

Python, 14 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import string

class Translator:
    allchars = string.maketrans('','')
    def __init__(self, frm='', to='', delete='', keep=None):
        if len(to) == 1:
            to = to * len(frm)
        self.trans = string.maketrans(frm, to)
        if keep is None:
            self.delete = delete
        else:
            self.delete = self.allchars.translate(self.allchars, keep.translate(self.allchars, delete))
    def __call__(self, s):
        return s.translate(self.trans, self.delete)

This class handles the three most common cases where I find myself having to stop and think about how to use translate:

1) Keeping only a given set of characters.

>>> trans = Translator(keep=string.digits)
>>> trans('Chris Perkins : 224-7992')
'2247992'

2) Deleting a given set of characters.

>>> trans = Translator(delete=string.digits)
>>> trans('Chris Perkins : 224-7992')
'Chris Perkins : -'

3) Replacing a set of characters with a single character.

>>> trans = Translator(string.digits, '#')
>>> trans('Chris Perkins : 224-7992')
'Chris Perkins : ###-####'

This last one is admittedly a bit of a special case, but it is something I seem to need to do once in a while.

Note that the delete parameter trumps the keep parameter if they overlap:

>>> trans = Translator(delete='abcd', keep='cdef')
>>> trans('abcdef')
'ef'
This is an arbitrary design choice - makes as much sense as anything, I suppose.

See also: 1) http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/101521 for an excellent explanation of maketrans and translate. 2) http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/59857 for an equivalent to Translate(keep=whatever).

3 comments

Raymond Hettinger 19 years, 7 months ago  # | flag

Alternate implementation using nested functions. Nice utility!

Here is a drop-in replacement using functions and nested scopes instead of class logic. It is a little bit less wordy and runs a bit faster:

import string

def Translator(frm='', to='', delete='', keep=None):
    allchars = string.maketrans('','')
    if len(to) == 1:
        to = to * len(frm)
    trans = string.maketrans(frm, to)
    if keep is not None:
        delete = allchars.translate(allchars, keep.translate(allchars, delete))
    def callable(s):
        return s.translate(trans, delete)
    return callable
Phillip Ruggera 19 years, 3 months ago  # | flag

Try a list comprehension.

The string module is being depreciated so you need to
start using list comprehensions to do these things:

>>> str = 'Chris Perkins : 224-7992'
>>> set = '0123456789'

1) Keeping only a given set of characters.

>>> ''.join([c for c in str if c in set])
'2247992'

2) Deleting a given set of characters.

>>> ''.join([c for c in str if c not in set])
'Chris Perkins : -'
victor chung 12 years ago  # | flag

Your script is helpful.

You might want to assert len(frm) = len(to) before line 8