用string.translate()将不可打印字符转换为点

2 投票
4 回答
2672 浏览
提问于 2025-04-15 16:23

我之前做过这个,结果发现这段代码看起来很丑,明明是个看似简单的任务。

我的目标是把任何不可打印的字符都转换成一个.(点)。在我看来,“可打印”的定义是排除掉一些特殊字符,比如换行符、制表符等等。这是为了打印像老版MS-DOS调试的“十六进制转储”格式……或者类似的东西(因为多余的空格会搞乱原本的转储布局)。

我知道可以用string.translate(),但使用这个方法需要一个翻译表。所以我用string.maketrans()来创建这个表。以下是我能想到的最佳方案:

filter = string.maketrans(
   string.translate(string.maketrans('',''),
   string.maketrans('',''),string.printable[:-5]),
   '.'*len(string.translate(string.maketrans('',''),
   string.maketrans('',''),string.printable[:-5])))

……这段代码看起来真是乱七八糟(不过确实能用)。

接下来你可以用类似这样的方式调用:

for each_line in sometext:
    print string.translate(each_line, filter)

……这样就可以高兴地使用了。(只要你不去看底层实现)。

如果我把那段可怕的表达式拆分成几个单独的语句,代码会更易读:

ascii = string.maketrans('','')   # The whole ASCII character set
nonprintable = string.translate(ascii, ascii, string.printable[:-5])  # Optional delchars argument
filter = string.maketrans(nonprintable, '.' * len(nonprintable))

这样做确实很诱人,主要是为了让代码更清晰。

不过,我一直在想,难道没有更优雅的方式来表达这个吗!

4 个回答

1

我觉得这个解决方案并不难看。它肯定比任何基于正则表达式的解决方案要高效得多。这里有一个稍微短一点的解决方案,不过只适用于python2.6:

nonprintable = string.maketrans('','').translate(None, string.printable[:-5])
filter = string.maketrans(nonprintable, '.' * len(nonprintable))
4

这里提到的“ascii”是个比较广泛的用法,但你大概明白什么意思了。

>>> import string
>>> ascii="".join(map(chr,range(256)))
>>> filter="".join(('.',x)[x in string.printable[:-5]] for x in ascii)
>>> ascii.translate(filter)
'................................ !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~.................................................................................................................................'

如果我在进行代码比赛的话,可能会用类似这样的写法:

filter='.'*32+"".join(map(chr,range(32,127)))+'.'*129
5

这里有另一种方法,使用列表推导式:

filter = ''.join([['.', chr(x)][chr(x) in string.printable[:-5]] for x in xrange(256)])

撰写回答