从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> </p>\n\t<p> </p>\n\t<p> </p>\n\t<p> </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题:字符串中的单词数:统计字符串中的单词个数,这里的单词指的是连续的不是空格的字符。 请注意,你可以假定字符串里不包括任何不可打印的字符。