98

我用来跑步

$s =~ s/[^[:print:]]//g;

在 Perl 上摆脱不可打印的字符。

在 Python 中没有 POSIX 正则表达式类,我不能写 [:print:] 让它意味着我想要的。我知道在 Python 中无法检测字符是否可打印。

你会怎么办?

编辑:它也必须支持 Unicode 字符。string.printable 方式会很高兴地将它们从输出中剥离出来。curses.ascii.isprint 将为任何 unicode 字符返回 false。

4

14 回答 14

90

不幸的是,在 Python 中迭代字符串相当慢。对于这种事情,正则表达式的速度要快一个数量级。您只需要自己构建角色类。unicodedata模块对此非常有帮助,尤其是unicodedata.category()函数。有关类别的描述,请参阅Unicode 字符数据库

import unicodedata, re, itertools, sys

all_chars = (chr(i) for i in range(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
# or equivalently and much more efficiently
control_chars = ''.join(map(chr, itertools.chain(range(0x00,0x20), range(0x7f,0xa0))))

control_char_re = re.compile('[%s]' % re.escape(control_chars))

def remove_control_chars(s):
    return control_char_re.sub('', s)

对于 Python2

import unicodedata, re, sys

all_chars = (unichr(i) for i in xrange(sys.maxunicode))
categories = {'Cc'}
control_chars = ''.join(c for c in all_chars if unicodedata.category(c) in categories)
# or equivalently and much more efficiently
control_chars = ''.join(map(unichr, range(0x00,0x20) + range(0x7f,0xa0)))

control_char_re = re.compile('[%s]' % re.escape(control_chars))

def remove_control_chars(s):
    return control_char_re.sub('', s)

对于某些用例,额外的类别(例如来自对照组的所有类别可能更可取,尽管这可能会减慢处理时间并显着增加内存使用量。每个类别的字符数:

  • Cc(控制):65
  • Cf(格式):161
  • Cs(代理):2048
  • Co(自用):137468
  • Cn(未分配):836601

编辑从评论中添加建议。

于 2008-09-18T14:28:04.380 回答
79

据我所知,最pythonic/最有效的方法是:

import string

filtered_string = filter(lambda x: x in string.printable, myStr)
于 2008-09-18T13:23:14.690 回答
20

您可以尝试使用以下功能设置过滤器unicodedata.category()

import unicodedata
printable = {'Lu', 'Ll'}
def filter_non_printable(str):
  return ''.join(c for c in str if unicodedata.category(c) in printable)

有关可用类别,请参见Unicode 数据库字符属性中第 175 页的表 4-9

于 2008-09-18T15:25:37.923 回答
13

在 Python 3 中,

def filter_nonprintable(text):
    import itertools
    # Use characters of control category
    nonprintable = itertools.chain(range(0x00,0x20),range(0x7f,0xa0))
    # Use translate to remove all non-printable characters
    return text.translate({character:None for character in nonprintable})

有关.translate() 与 regex & .replace() 的比较,请参阅有关删除标点符号的 StackOverflow 帖子

可以通过nonprintable = (ord(c) for c in (chr(i) for i in range(sys.maxunicode)) if unicodedata.category(c)=='Cc')使用@Ants Aasma 所示的Unicode 字符数据库类别来生成范围。

于 2014-09-14T02:20:39.790 回答
13

以下将适用于 Unicode 输入,并且速度相当快......

import sys

# build a table mapping all non-printable characters to None
NOPRINT_TRANS_TABLE = {
    i: None for i in range(0, sys.maxunicode + 1) if not chr(i).isprintable()
}

def make_printable(s):
    """Replace non-printable characters in a string."""

    # the translate method on str removes characters
    # that map to None from the string
    return s.translate(NOPRINT_TRANS_TABLE)


assert make_printable('Café') == 'Café'
assert make_printable('\x00\x11Hello') == 'Hello'
assert make_printable('') == ''

我自己的测试表明这种方法比遍历字符串并使用str.join.

于 2019-01-31T00:58:09.353 回答
7

python 3中的另一个选项:

re.sub(f'[^{re.escape(string.printable)}]', '', my_string)
于 2018-09-27T15:16:00.670 回答
6

此函数使用列表推导和 str.join,因此它以线性时间而不是 O(n^2) 运行:

from curses.ascii import isprint

def printable(input):
    return ''.join(char for char in input if isprint(char))
于 2008-09-18T13:26:00.143 回答
4

根据@Ber 的回答,我建议仅删除Unicode 字符数据库类别中定义的控制字符:

import unicodedata
def filter_non_printable(s):
    return ''.join(c for c in s if not unicodedata.category(c).startswith('C'))
于 2020-06-23T08:25:06.103 回答
2

我现在想出的最好的是(感谢上面的python-izers)

def filter_non_printable(str):
  return ''.join([c for c in str if ord(c) > 31 or ord(c) == 9])

这是我发现适用于 Unicode 字符/字符串的唯一方法

有更好的选择吗?

于 2008-09-18T13:17:35.843 回答
2

下面的一个比上面的其他执行得更快。看一看

''.join([x if x in string.printable else '' for x in Str])
于 2018-01-07T02:13:54.950 回答
2

在 Python 中没有 POSIX 正则表达式类

使用regex库时有:https ://pypi.org/project/regex/

它维护良好,支持 Unicode 正则表达式、Posix 正则表达式等等。用法(方法签名)与 Pythonre.

从文档中:

[[:alpha:]]; [[:^alpha:]]

支持 POSIX 字符类。这些通常被视为\p{...}.

(我不是附属的,只是一个用户。)

于 2018-07-05T07:04:04.770 回答
0

要删除“空白”,

import re
t = """
\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>&nbsp;</p>\n\t<p>
"""
pat = re.compile(r'[\t\n]')
print(pat.sub("", t))
于 2017-09-11T05:22:02.680 回答
0

改编自Ants Aasmashawnrad的回答:

nonprintable = set(map(chr, list(range(0,32)) + list(range(127,160))))
ord_dict = {ord(character):None for character in nonprintable}
def filter_nonprintable(text):
    return text.translate(ord_dict)

#use
str = "this is my string"
str = filter_nonprintable(str)
print(str)

在 Python 3.7.7 上测试

于 2020-06-17T19:42:17.433 回答
0

从 python 中的字符串中剥离“不可打印”字符的优雅 pythonic 解决方案是使用 isprintable() 字符串方法以及生成器表达式或列表理解,具体取决于用例,即。字符串的大小:

    ''.join(c for c in my_string if c.isprintable())

str.isprintable() 如果字符串中的所有字符都可打印或字符串为空,则返回 True,否则返回 False。不可打印字符是那些在 Unicode 字符数据库中定义为“其他”或“分隔符”的字符,但 ASCII 空间 (0x20) 除外,它被认为是可打印的。(请注意,此上下文中的可打印字符是在字符串上调用 repr() 时不应转义的字符。它与写入 sys.stdout 或 sys.stderr 的字符串的处理无关。)

于 2022-02-13T23:24:50.180 回答