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

我用来跑步

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

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

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

你会怎么做?

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

【问题讨论】:

    标签: python string non-printable


    【解决方案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() 不会工作得更快吗?见stackoverflow.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 StackOverflow 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'。它表现更好吗?如果您更喜欢执行速度而不是内存要求,可以预先计算表格,如stackoverflow.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 上测试

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-04-08
          • 1970-01-01
          • 2012-06-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多