从python中的字符串中去除不可打印的字符

Posted

技术标签:

【中文标题】从python中的字符串中去除不可打印的字符【英文标题】:Stripping non printable characters from a string in python 【发布时间】:2010-09-10 16:24:02 【问题描述】:

我用来跑步

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

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

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

你会怎么做?

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

【问题讨论】:

【参考方案1】:

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

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)

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

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

编辑添加来自 cmets 的建议。

【讨论】:

这里的“抄送”够吗?我不知道,我只是在问——在我看来,其他一些“C”类别也可能是这个过滤器的候选者。 这个函数在发布时会删除一半的希伯来字符。对于给定的两种方法,我得到了相同的效果。 从性能的角度来看,在这种情况下 string.translate() 不会工作得更快吗?见***.com/questions/265960/… 使用all_chars = (unichr(i) for i in xrange(sys.maxunicode)) 避免狭窄的构建错误。 对我来说control_chars == '\x00-\x1f\x7f-\x9f'(在 Python 3.5.2 上测试)【参考方案2】:

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

import string

filtered_string = filter(lambda x: x in string.printable, myStr)

【讨论】:

你可能想要filtered_string = ''.join(filter(lambda x:x in string.printable, myStr) 这样你才能得到一个字符串。 遗憾的是 string.printable 不包含 unicode 字符,因此 ü 或 ó 不会出现在输出中......也许还有别的东西? 您应该使用列表推导式或生成器表达式,而不是 filter + lambda。其中之一将在 99.9% 的时间内更快。 ''.join(s for s in myStr if s in string.printable) @AaronGallagher:快 99.9%?你从哪里摘下这个数字?性能比较远没有那么糟糕。 嗨威廉。此方法似乎删除了所有非 ASCII 字符。 Unicode 中有很多可打印的非 ASCII 字符!【参考方案3】:

您可以尝试使用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 database character properties 中第 175 页的表 4-9

【讨论】:

你开始了一个列表理解,它没有在你的最后一行结束。我建议您完全删除左括号。 感谢您指出这一点。我相应地编辑了帖子 这似乎是最直接、最直接的方法。谢谢。 @CsabaToth 这三个都是有效的并且产生相同的集合。您的可能是指定集合文字的最佳方式。 @AnubhavJhalani 您可以向过滤器添加更多 Unicode 类别。要保留除字母之外的空格和数字,请使用 printable = 'Lu', 'Ll', Zs', 'Nd'【参考方案4】:

以下内容适用于 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 返回结果的函数更快。

【讨论】:

这是唯一适用于 unicode 字符的答案。太棒了,您提供了测试用例! 如果要允许换行,请在建表时添加LINE_BREAK_CHARACTERS = set(["\n", "\r"])and not chr(i) in LINE_BREAK_CHARACTERS【参考方案5】:

在 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)

请参阅this *** post on removing punctuation 了解 .translate() 与正则表达式和 .replace() 的比较

范围可以通过nonprintable = (ord(c) for c in (chr(i) for i in range(sys.maxunicode)) if unicodedata.category(c)=='Cc') 使用Unicode character database categories 生成,如@Ants Aasma 所示。

【讨论】:

最好使用 Unicode 范围(参见@Ants Aasma 的回答)。结果将是text.translate(c:None for c in itertools.chain(range(0x00,0x20),range(0x7f,0xa0)))【参考方案6】:

python 3 中的另一个选项:

re.sub(f'[^re.escape(string.printable)]', '', my_string)

【讨论】:

这对我和它的 1 行非常有用。谢谢 由于某种原因,这在 Windows 上效果很好,但不能在 linux 上使用,我不得不将 f 更改为 r,但我不确定这是否是解决方案。 听起来你的 Linux Python 太旧了,无法支持 f-strings。 r-strings 完全不同,尽管你可以说r'[^' + re.escape(string.printable) + r']'。 (我不认为re.escape() 在这里是完全正确的,但如果它有效......) 遗憾的是 string.printable 不包含 unicode 字符,因此 ü 或 ó 不会出现在输出中...【参考方案7】:

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

from curses.ascii import isprint

def printable(input):
    return ''.join(char for char in input if isprint(char))

【讨论】:

【参考方案8】:

根据@Ber 的回答,我建议只删除Unicode character database categories 中定义的控制字符:

import unicodedata
def filter_non_printable(s):
    return ''.join(c for c in s if not unicodedata.category(c).startswith('C'))

【讨论】:

这是一个很好的答案! 您可能对startswith('C') 有所了解,但在我的测试中,这比任何其他解决方案的性能都要差。 big-mclargehuge:我的解决方案的目标是结合完整性和简单性/可读性。您可以尝试改用if unicodedata.category(c)[0] != 'C'。它表现更好吗?如果您更喜欢执行速度而不是内存要求,可以预先计算表格,如***.com/a/93029/3779655中所示【参考方案9】:

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

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

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

还有更好的选择吗?

【讨论】:

除非您使用的是 python 2.3,否则内部 [] 是多余的。 "return ''.join(c for c ...)" 不是很多余——尽管最终结果是相同的,但它们具有不同的含义(和性能特征)。 范围的另一端是否也应该不受保护?:“ord(c) 但也有不可打印的 Unicode 字符。【参考方案10】:

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

''.join([x if x in string.printable else '' for x in Str])

【讨论】:

"".join([c if 0x21<=ord(c) and ord(c)<=0x7e else "" for c in ss])【参考方案11】:

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

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

它维护良好,支持 Unicode 正则表达式、Posix 正则表达式等等。用法(方法签名)非常类似于 Python 的 re

来自文档:

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

支持 POSIX 字符类。这些 通常被视为\p... 的替代形式。

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

【讨论】:

【参考方案12】:

要删除“空白”,

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))

【讨论】:

其实你也不需要方括号了。【参考方案13】:

改编自 Ants Aasma 和 shawnrad 的回答:

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 上测试

【讨论】:

以上是关于从python中的字符串中去除不可打印的字符的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode练习(Python):第434题:字符串中的单词数:统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。 请注意,你可以假定字符串里不包括任何不可打印的字符。

Leetcode练习(Python):第434题:字符串中的单词数:统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。 请注意,你可以假定字符串里不包括任何不可打印的字符。

在Python中从字符串中去除数字[重复]

如何从 OCaml 中的字符串中去除空格?

python 去除字符串中的空格

Redshift:如何删除不可打印的字符