【问题标题】:How do you filter a string such that only characters in your list are returned?您如何过滤字符串以仅返回列表中的字符?
【发布时间】:2010-10-26 14:41:00
【问题描述】:

想象一个字符串,例如 'Agh#$%#%2341- -!zdrkfd',我只希望对其执行一些操作,以便只返回小写字母(例如),在这种情况下会带上“ghzdrkfd”。

你如何在 Python 中做到这一点?显而易见的方法是创建一个字符列表,从“a”到“z”,然后遍历我的字符串中的字符并构建一个新的字符串,一个字符一个字符,只包含我列表中的那些。这看起来很原始。

我想知道正则表达式是否合适。替换不需要的字符似乎有问题,我倾向于将白名单列入黑名单。 .match 函数似乎不合适。我查看了 Python 网站上的相应页面,但没有找到合适的方法。

如果正则表达式不合适并且正确的方法是循环,是否有一个简单的函数可以将字符串“分解”成列表?还是我只是在那里打了另一个 for 循环?

【问题讨论】:

标签: python filter whitelist


【解决方案1】:

采用inputstring 并针对whitelist 字符进行过滤的更通用且易于理解的解决方案:

inputstring = "Agh#$%#%2341- -!zdrkfd"
whitelist = "abcdefghijklmnopqrstuvwxyz"
remove = inputstring.translate(None, whitelist)
result = inputstring.translate(None, remove)
print result

打印出来

ghzdrkfd

第一个string.translate 从输入字符串中删除白名单中的所有字符。这给了我们想要删除的字符。第二个string.translate 调用将那些从输入字符串中删除并产生所需的结果。

【讨论】:

    【解决方案2】:
    import string
    
    print filter(string.lowercase.__contains__, "lowerUPPER")
    print filter("123".__contains__, "a1b2c3")
    

    【讨论】:

      【解决方案3】:
      s = 'ASDjifjASFJ7364'
      s_lowercase = ''.join(filter(lambda c: c.islower(), s))
      print s_lowercase #print 'jifj'
      

      【讨论】:

      • s 上不需要调用列表。字符串对象是可迭代的。
      【解决方案4】:

      使用正则表达式很容易,尤其是在这种情况下:

      >>> import re
      >>> s = 'ASDjifjASFJ7364'
      >>> re.sub(r'[^a-z]+', '', s)
      'jifj'
      

      如果你打算多次这样做,最好事先编译正则表达式:

      >>> import re
      >>> s = 'ASDjifjASFJ7364'
      >>> r = re.compile(r'[^a-z]+')
      >>> r.sub('', s)
      'jifj'
      

      【讨论】:

      • 公平地说,我在你的预编译版本上再次运行了测试,它仍然比翻译慢。
      • 正则表达式应该是 '[^a-z]+' - 这会显着提高性能。
      • @gnud,你对提高性能是正确的。但它仍然比翻译慢得多。
      【解决方案5】:

      如果您正在寻找效率。使用translate 函数是最快的。

      可用于快速替换字符和/或删除字符。

      import string
      delete_table  = string.maketrans(
          string.ascii_lowercase, ' ' * len(string.ascii_lowercase)
      )
      table = string.maketrans('', '')
      
      "Agh#$%#%2341- -!zdrkfd".translate(table, delete_table)
      

      在 python 2.6 中:您不再需要第二个表

      import string
      delete_table  = string.maketrans(
          string.ascii_lowercase, ' ' * len(string.ascii_lowercase)
      )
      "Agh#$%#%2341- -!zdrkfd".translate(None, delete_table)
      

      这种方法比其他任何方法都快。当然,您需要将 delete_table 存储在某处并使用它。但即使您不存储它并每次都构建它,它仍然会比迄今为止其他建议的方法更快。

      为了确认我的声明,这里是结果:

      for i in xrange(10000):
          ''.join(c for c in s if c.islower())
      
      real    0m0.189s
      user    0m0.176s
      sys 0m0.012s
      

      运行正则表达式解决方案时:

      for i in xrange(10000):
          re.sub(r'[^a-z]', '', s)
      
      real    0m0.172s
      user    0m0.164s
      sys 0m0.004s
      

      [根据要求]如果您预编译正则表达式:

      r = re.compile(r'[^a-z]')
      for i in xrange(10000):
          r.sub('', s)
      
      real    0m0.166s
      user    0m0.144s
      sys 0m0.008s
      

      以相同的次数运行 translate 方法:

      real    0m0.075s
      user    0m0.064s
      sys 0m0.012s
      

      【讨论】:

      • 公平地说,您应该在循环外编译正则表达式。
      • 我正在比较推荐的最佳解决方案。 Paolo Bergantino 就是这样写的。
      • 我把它写成一次性解决方案,显然最好编译它,所以你应该这样比较。
      • 为了公平起见,将正则表达式更改为 '[^a-z]+'。这样,它将一次性替换一系列匹配项,而不是一次替换一个字符。
      • @gnud,我试过了,它有点快但翻译不匹配。顺便说一句,字符串越大,翻译和其他方法在性能上的差异就越大。处理时间几乎不会随着翻译中的字符串长度而增加。
      【解决方案6】:

      如果您对处理字符串特别感兴趣,这里有一个解决方案:

       s = 'Agh#$%#%2341- -!zdrkfd'
       lowercase_chars = [chr(i) for i in xrange(ord('a'), ord('z') + 1)]
       whitelist = set(lowercase_chars)
       filtered_list = [c for c in s if c in whitelist]
      

      为了效率,白名单其实是一个集合(不是列表)。

      如果你需要一个字符串,使用join():

      filtered_str = ''.join(filtered_list)
      

      filter() 是一个更通用的解决方案。来自文档(http://docs.python.org/library/functions.html):

      过滤器(函数,可迭代)

      从函数返回 true 的那些可迭代元素构造一个列表。 iterable 可以是序列、支持迭代的容器或迭代器。如果 iterable 是字符串或元组,则结果也具有该类型;否则它总是一个列表。如果function为None,则假定恒等函数,即iterable中所有为false的元素都被移除。

      这将是使用 filter() 的一种方式:

      filtered_list = filter(lambda c: c.islower(), s)
      

      【讨论】:

        【解决方案7】:
        import string
        print "".join([c for c in "Agh#$%#%2341- -!zdrkfd" if c in string.lowercase])
        

        【讨论】:

          【解决方案8】:
          >>> s = 'Agh#$%#%2341- -!zdrkfd'
          >>> ''.join(i for i in s if  i in 'qwertyuiopasdfghjklzxcvbnm')
          'ghzdrkfd'
          

          【讨论】:

            【解决方案9】:

            我会使用正则表达式。对于小写匹配 [a-z]。

            【讨论】:

              【解决方案10】:
              s = 'Agh#$%#%2341- -!zdrkfd'  
              print ''.join(c for c in s if c.islower())
              

              字符串对象是可迭代的;无需将字符串“分解”成列表。您可以在列表推导中添加任何您想要的条件,它会相应地过滤字符。

              您也可以使用正则表达式来实现这一点,但这只会隐藏循环。正则表达式库仍然需要遍历字符串中的字符才能过滤它们。

              【讨论】:

              • isalpha() 不需要,因为非字母字符会在 islower() 上返回 false
              • 这也可以通过将c.islower()更改为例如修改为使用自定义字符列表。 c in "abcDEF".
              • 好吧,我以为我有更好的答案,但这更简单。结合 Ben Blank 的评论使答案具有适当的普遍性。我以为我必须先列出我的清单,但根本没有。
              猜你喜欢
              • 2017-05-10
              • 1970-01-01
              • 2013-09-24
              • 1970-01-01
              • 1970-01-01
              • 2023-04-03
              • 1970-01-01
              • 2021-12-25
              • 1970-01-01
              相关资源
              最近更新 更多