【问题标题】:Remove specific characters from a string in Python从 Python 中的字符串中删除特定字符
【发布时间】:2011-04-25 18:12:29
【问题描述】:

我正在尝试使用 Python 从字符串中删除特定字符。这是我现在正在使用的代码。不幸的是,它似乎对字符串没有任何作用。

for char in line:
    if char in " ?.!/;:":
        line.replace(char,'')

我该如何正确地做到这一点?

【问题讨论】:

标签: python string replace immutability


【解决方案1】:

Python 中的字符串是不可变的(无法更改)。正因为如此,line.replace(...) 的作用只是创建一个新字符串,而不是更改旧字符串。您需要将其重新绑定(分配)给line,以便让该变量采用新值,并删除这些字符。

此外,相对而言,您执行此操作的方式会有点慢。对于经验丰富的 Python 开发者来说,这也可能会有点令人困惑,他们会看到一个双重嵌套的结构,并会认为正在发生更复杂的事情。

从 Python 2.6 和更新的 Python 2.x 版本 * 开始,您可以改用 str.translate,(请参阅下面的 Python 3 答案):

line = line.translate(None, '!@#$')

或用re.sub替换正则表达式

import re
line = re.sub('[!@#$]', '', line)

括号中的字符构成一个字符类line 中属于该类的任何字符都将替换为 sub 的第二个参数:一个空字符串。

Python 3 答案

在 Python 3 中,字符串是 Unicode。你必须翻译得有点不同。 kevpie 在其中一个答案的comment 中提到了这一点,并在documentation for str.translate 中注明。

当调用 Unicode 字符串的translate 方法时,不能传递我们上面使用的第二个参数。您也不能将None 作为第一个参数传递。相反,您将翻译表(通常是字典)作为唯一参数传递。此表将字符的序数值(即在它们上调用ord 的结果)映射到应该替换它们的字符的序数值,或者——对我们有用——None 来表示他们应该被删除。

因此,要使用 Unicode 字符串进行上述舞蹈,您可以调用类似

translation_table = dict.fromkeys(map(ord, '!@#$'), None)
unicode_line = unicode_line.translate(translation_table)

这里dict.fromkeysmap用于简洁地生成包含

的字典
{ord('!'): None, ord('@'): None, ...}

更简单,如another answer puts it,就地创建转换表:

unicode_line = unicode_line.translate({ord(c): None for c in '!@#$'})

或者,正如Joseph Lee 所提出的,创建与str.maketrans 相同的转换表:

unicode_line = unicode_line.translate(str.maketrans('', '', '!@#$'))

* 为了与早期的 Python 兼容,您可以创建一个“空”转换表来代替 None

import string
line = line.translate(string.maketrans('', ''), '!@#$')

这里string.maketrans是用来创建一个翻译表的,它只是一个包含序数值0到255的字符的字符串。

【讨论】:

  • 在 Python3 中,line.translate 只接受一个参数,第一个解决方案不起作用
  • 在 python3 中,str.translate() 不接受第二个参数。所以,你的答案会变成line.translate({ord(i):None for i in '!@#$'})
  • 与任何其他字符相同。 Python 允许您使用成对的单引号或双引号。所以你只需为字符集写"'"
  • @naveen 上面的评论对我有用。 Pythony 2.7.13。就我而言,我想去掉 " 和 ' 字符:notes = notes.translate({ord(i):None for i in '\"\''})
  • 在 Python 3 中,您可以使用unicode_line.translate(str.maketrans('', '', '!@#$'))。或unicode_line.translate(dict.fromkeys(map(ord, '!@#$')))
【解决方案2】:

试试这个:

def rm_char(original_str, need2rm):
    ''' Remove charecters in "need2rm" from "original_str" '''
    return original_str.translate(str.maketrans('','',need2rm))

此方法在 Python 3 中运行良好

【讨论】:

  • 这似乎是这个问题的最佳答案。
  • 当然。我将它推广到 Python 3 中。
【解决方案3】:

我是否错过了这里的重点,或者只是以下内容:

string = "ab1cd1ef"
string = string.replace("1", "") 

print(string)
# result: "abcdef"

把它放在一个循环中:

a = "a!b@c#d$"
b = "!@#$"
for char in b:
    a = a.replace(char, "")

print(a)
# result: "abcd"

【讨论】:

  • 这将在每个循环中复制字符串,这可能是不可取的。它也不是很好的 Python。在 Python 中,你会像这样循环:for char in b: a=a.replace(char,"")
  • 为了更高效,将b中的所有字符放入一个集合中,初始化一个空列表,然后迭代a,如果当前字符不在集合中,则添加到列表中。完成后,您可以将列表转换回字符串。线性时间和线性(额外)空间。
  • 当然,这行得通,但它的时间复杂度为 O, O(n^2) 其中 n = len(string)
【解决方案4】:

带有re.sub正则表达式

从 Python 3.5 开始,可以使用正则表达式 re.sub 进行替换:

import re
re.sub('\ |\?|\.|\!|\/|\;|\:', '', line)

示例

import re
line = 'Q: Do I write ;/.??? No!!!'
re.sub('\ |\?|\.|\!|\/|\;|\:', '', line)

'QDoIwriteNo'

说明

regular expressions(正则表达式)中,| 是逻辑或,\ 转义可能是实际正则表达式命令的空格和特殊字符。而sub 代表替换, 在本例中为空字符串''

【讨论】:

  • @vitaliis 如果您还想删除换行符和回车符,请将re.sub() 中的第一个字符串替换为'\ |\?|\.|\!|\/|\;|\:|\n|\r'
【解决方案5】:

如果你希望你的字符串只是使用 ASCII 码允许的字符,你可以使用这段代码:

for char in s:
    if ord(char) < 96 or ord(char) > 123:
        s = s.replace(char, "")

它将删除 a....z 之外的所有字符,甚至大写。

【讨论】:

    【解决方案6】:

    字符串在 Python 中是不可变的。 replace 方法在替换后返回一个新字符串。试试:

    for char in line:
        if char in " ?.!/;:":
            line = line.replace(char,'')
    

    这与您的原始代码相同,只是在循环内添加了对 line 的赋值。

    请注意,字符串replace() 方法会替换字符串中出现的所有个字符,因此您可以对要删除的每个字符使用replace(),而不是循环遍历字符串中的每个字符。

    【讨论】:

    • 如何在迭代行的同时进行修改?
    • @eumiro:迭代继续在原始 line
    • 很高兴知道!因此,如果我迭代一个数组,我就会迭代一个原始数组。不可能对迭代器进行迭代。
    • 这很浪费。您遍历line 的每个字符并检查该字符是否在要删除的字符集中。如果是,您删除 all 它在line 中出现的所有字符,那么为什么要继续检查line 中的其余字符并再次检查保证不存在的字符更长?相反,我建议这样做:for char in " ?.!/;:": line = line.replace(char, "") 这将有与要删除的字符一样多的迭代轮次。 More legible version here
    【解决方案7】:

    字符串方法replace不会修改原始字符串。它会保留原件并返回修改后的副本。

    你想要的是这样的:line = line.replace(char,'')

    def replace_all(line, )for char in line:
        if char in " ?.!/;:":
            line = line.replace(char,'')
        return line
    

    但是,每次删除字符时都创建一个新字符串是非常低效的。我建议改为:

    def replace_all(line, baddies, *):
        """
        The following is documentation on how to use the class,
        without reference to the implementation details:
    
        For implementation notes, please see comments begining with `#`
        in the source file.
    
        [*crickets chirp*]
    
        """
    
        is_bad = lambda ch, baddies=baddies: return ch in baddies
        filter_baddies = lambda ch, *, is_bad=is_bad: "" if is_bad(ch) else ch
        mahp = replace_all.map(filter_baddies, line)
        return replace_all.join('', join(mahp))
    
        # -------------------------------------------------
        # WHY `baddies=baddies`?!?
        #     `is_bad=is_bad`
        # -------------------------------------------------
        # Default arguments to a lambda function are evaluated
        # at the same time as when a lambda function is
        # **defined**.
        #
        # global variables of a lambda function
        # are evaluated when the lambda function is
        # **called**
        #
        # The following prints "as yellow as snow"
        #
        #     fleece_color = "white"
        #     little_lamb = lambda end: return "as " + fleece_color + end
        #
        #     # sometime later...
        #
        #     fleece_color = "yellow"
        #     print(little_lamb(" as snow"))
        # --------------------------------------------------
    replace_all.map = map
    replace_all.join = str.join
    

    【讨论】:

      【解决方案8】:

      即使下面的方法也有效

      line = "a,b,c,d,e"
      alpha = list(line)
              while ',' in alpha:
                  alpha.remove(',')
      finalString = ''.join(alpha)
      print(finalString)
      

      输出:abcde

      【讨论】:

        【解决方案9】:

        #为目录中的每个文件,重命名文件名

           file_list = os.listdir (r"D:\Dev\Python")
        
           for file_name in file_list:
        
               os.rename(file_name, re.sub(r'\d+','',file_name))
        

        【讨论】:

          【解决方案10】:

          您可以使用 re 模块的正则表达式替换。使用 ^ 表达式可以让您从字符串中准确选择所需内容。

              import re
              text = "This is absurd!"
              text = re.sub("[^a-zA-Z]","",text) # Keeps only Alphabets
              print(text)
          

          输出将是“Thisisabsurd”。只有在 ^ 符号之后指定的内容才会出现。

          【讨论】:

            【解决方案11】:

            这里有一些可能的方法来完成这个任务:

            def attempt1(string):
                return "".join([v for v in string if v not in ("a", "e", "i", "o", "u")])
            
            
            def attempt2(string):
                for v in ("a", "e", "i", "o", "u"):
                    string = string.replace(v, "")
                return string
            
            
            def attempt3(string):
                import re
                for v in ("a", "e", "i", "o", "u"):
                    string = re.sub(v, "", string)
                return string
            
            
            def attempt4(string):
                return string.replace("a", "").replace("e", "").replace("i", "").replace("o", "").replace("u", "")
            
            
            for attempt in [attempt1, attempt2, attempt3, attempt4]:
                print(attempt("murcielago"))
            

            PS:而不是使用“?.!/;:”示例使用元音......是的,“murcielago”是西班牙语单词,表示蝙蝠......有趣的词,因为它包含所有元音:)

            PS2:如果您对性能感兴趣,可以使用以下简单代码来衡量这些尝试:

            import timeit
            
            
            K = 1000000
            for i in range(1,5):
                t = timeit.Timer(
                    f"attempt{i}('murcielago')",
                    setup=f"from __main__ import attempt{i}"
                ).repeat(1, K)
                print(f"attempt{i}",min(t))
            

            在我的盒子里你会得到:

            attempt1 2.2334518376057244
            attempt2 1.8806643818474513
            attempt3 7.214925774955572
            attempt4 1.7271184513757465
            

            因此,对于这个特定的输入,尝试 4 似乎是最快的。

            【讨论】:

            • 您在attempt1 中创建了一个不必要的list,并且为简单起见,可以将元组重写为"aeiou"(删除[] 将变成生成器而不创建一个列表)。您在attemt2 中创建了大量一次性中间字符串,您在attempt3 中使用了多个正则表达式应用程序,您可以一次性使用r'[aeiou]'。每个都有缺陷 - 很高兴看到不同的做事方式,但请修复它们以成为好的尝试
            • @PatrickArtner 你是绝对正确的......从我想到的完成这项任务的几十种方法中,我选择了较慢的方法(想向 OP 展示一些最简单的方法) ...也就是说,在你们关闭另一个线程之后,我失去了在这个已经回答的旧线程上投入更多精力的动力,所以...... :) 。不过感谢您的积分。
            • @PatrickArtner 好的...只是为了添加一个新的,“attempt4”...尚未测量,但我认为应该是更快的一个
            • @PatrickArtner 已编辑...尝试 4 是少数尝试中最快的。无论如何,我不会在这些东西上浪费更多时间:)
            【解决方案12】:

            递归拆分: s=字符串; chars=要删除的字符

            def strip(s,chars):
            if len(s)==1:
                return "" if s in chars else s
            return strip(s[0:int(len(s)/2)],chars) +  strip(s[int(len(s)/2):len(s)],chars)
            

            示例:

            print(strip("Hello!","lo"))    #He!
            

            【讨论】:

              【解决方案13】:

              我很惊讶没有人推荐使用内置的 filter 函数。

                  import operator
                  import string # only for the example you could use a custom string
              
                  s = "1212edjaq"
              

              假设我们要过滤掉所有不是数字的东西。使用过滤器内置方法“......相当于生成器表达式(item for item in iterable if function(item))” [Python 3 Builtins: Filter]

                  sList = list(s)
                  intsList = list(string.digits)
                  obj = filter(lambda x: operator.contains(intsList, x), sList)))
              

              在 Python 3 中返回

                  >>  <filter object @ hex>
              

              要获得打印的字符串,

                  nums = "".join(list(obj))
                  print(nums)
                  >> "1212"
              

              我不确定 filter 在效率方面的排名如何,但在进行列表推导等时知道如何使用是一件好事。

              更新

              从逻辑上讲,由于过滤器有效,您还可以使用列表理解,并且根据我的阅读,它应该更有效,因为 lambda 是编程函数世界的华尔街对冲基金经理。另一个优点是它是一种不需要任何进口的单线。例如,使用上面定义的相同字符串 's',

                    num = "".join([i for i in s if i.isdigit()])
              

              就是这样。返回将是原始字符串中所有数字字符的字符串。

              如果您有一个特定的可接受/不可接受字符列表,您只需调整列表理解的“if”部分。

                    target_chars = "".join([i for i in s if i in some_list]) 
              

              或者,

                    target_chars = "".join([i for i in s if i not in some_list])
              

              【讨论】:

              • 如果您使用的是lambda,则没有理由使用operator.containslambda x: operator.contains(intsList, x) 应该拼写为 lambda x: x in intsList,或者如果你想进行 C 级检查,intsList.__contains__(根本没有 lambda)可以解决问题。
              【解决方案14】:

              使用filter,您只需要一行

              line = filter(lambda char: char not in " ?.!/;:", line)
              

              如果lambda返回True,这会将字符串视为可迭代并检查每个字符:

              >>> help(filter)
              Help on built-in function filter in module __builtin__:
              
              filter(...)
                  filter(function or None, sequence) -> list, tuple, or string
              
                  Return those items of sequence for which function(item) is true.  If
                  function is None, return the items that are true.  If sequence is a tuple
                  or string, return the same type, else return a list.
              

              【讨论】:

                【解决方案15】:

                在 Python 3.5 中

                例如,

                os.rename(file_name, file_name.translate({ord(c): None for c in '0123456789'}))
                

                从字符串中删除所有数字

                【讨论】:

                  【解决方案16】:

                  这是我的 Python 2/3 兼容版本。由于翻译 api 发生了变化。

                  def remove(str_, chars):
                      """Removes each char in `chars` from `str_`.
                  
                      Args:
                          str_: String to remove characters from
                          chars: String of to-be removed characters
                  
                      Returns:
                          A copy of str_ with `chars` removed
                  
                      Example:
                              remove("What?!?: darn;", " ?.!:;") => 'Whatdarn'
                      """
                      try:
                          # Python2.x
                          return str_.translate(None, chars)
                      except TypeError:
                          # Python 3.x
                          table = {ord(char): None for char in chars}
                          return str_.translate(table)
                  

                  【讨论】:

                  • 我会使用dict.fromkeys(map(ord, '!@#$')) 来创建地图。
                  • map 通常比 list/dict/set/generator 理解的可读性差。以至于 Guido 想删除 it from the language。使用fromkeys 也有点聪明,需要进行文档检查。
                  • @MartijnPieters:对于 Python 3,它应该只是 str.maketrans('', '', chars),它一次性处理 ord 转换和 dict 构造(更不用说意图更明显,因为它旨在与str.translate 配对。
                  • @ShadowRanger ...Joseph Lee已经回答了
                  • @Wolf:我专门针对 Martijn 的观点作出回应,而不是独立回答。比期望人们在不相关的答案中看到评论的改进更容易。
                  【解决方案17】:
                  >>> s = 'a1b2c3'
                  >>> ''.join(c for c in s if c not in '123')
                  'abc'
                  

                  【讨论】:

                  • 我的回答确实为原始问题提供了一个解决方案,但我也对为什么我的解决方案可能不理想的反馈感兴趣(也许也是 OP)。我是否应该创建一个新问题并在上下文中引用这个问题?
                  【解决方案18】:

                  我使用的方法可能不会那么有效,但它非常简单。我可以使用切片和格式化一次删除不同位置的多个字符。 这是一个例子:

                  words = "things"
                  removed = "%s%s" % (words[:3], words[-1:])
                  

                  这将导致 'removed' 包含单词 'this'。

                  格式化对于在打印字符串的中间打印变量非常有帮助。它可以使用 % 后跟变量的数据类型插入任何数据类型;所有数据类型都可以使用 %s,浮点数(又名小数)和整数可以使用 %d

                  切片可用于对字符串进行复杂的控制。当我输入 words[:3] 时,它允许我选择字符串中从开头(冒号在数字之前,这意味着“从开始到”)到第 4 个字符(包括第 4 个字符)。 3 等于第 4 位的原因是因为 Python 从 0 开始。然后,当我把 word[-1:] 放在末尾时,它表示倒数第二个字符(冒号在数字后面)。输入 -1 将使 Python 从最后一个字符开始计数,而不是第一个字符。同样,Python 将从 0 开始。所以,word[-1:] 基本上意味着“从倒数第二个字符到字符串的结尾。

                  因此,通过切断我要删除的字符之前的字符和之后的字符并将它们夹在一起,我可以删除不需要的字符。 把它想象成香肠。中间很脏,所以我想把它去掉。我只是切断了我想要的两端,然后将它们放在一起,中间没有不需要的部分。

                  如果我想删除多个连续字符,我只需在 [] (切片部分)中移动数字。或者,如果我想从不同位置删除多个字符,我可以简单地一次将多个切片夹在一起。

                  例子:

                   words = "control"
                   removed = "%s%s" % (words[:2], words[-2:])
                  

                  已移除等于“酷”。

                  words = "impacts"
                  removed = "%s%s%s" % (words[1], words[3:5], words[-1])
                  

                  已移除等于“macs”。

                  在这种情况下,[3:5] 表示 位置 3 的字符到 位置 5 的字符(不包括最后的字符位置)。

                  记住,Python 从 0 开始计数,所以你也需要这样做。

                  【讨论】:

                    【解决方案19】:

                    您还可以使用函数来使用列表替换不同类型的正则表达式或其他模式。有了它,您可以混合正则表达式、字符类和非常基本的文本模式。当您需要替换大量元素(如 HTML 元素)时,它非常有用。

                    *NB:适用于 Python 3.x

                    import re  # Regular expression library
                    
                    
                    def string_cleanup(x, notwanted):
                        for item in notwanted:
                            x = re.sub(item, '', x)
                        return x
                    
                    line = "<title>My example: <strong>A text %very% $clean!!</strong></title>"
                    print("Uncleaned: ", line)
                    
                    # Get rid of html elements
                    html_elements = ["<title>", "</title>", "<strong>", "</strong>"]
                    line = string_cleanup(line, html_elements)
                    print("1st clean: ", line)
                    
                    # Get rid of special characters
                    special_chars = ["[!@#$]", "%"]
                    line = string_cleanup(line, special_chars)
                    print("2nd clean: ", line)
                    

                    在函数 string_cleanup 中,它将您的字符串 x 和您不需要的列表作为参数。对于该元素或模式列表中的每个项目,如果需要替换,则会完成。

                    输出:

                    Uncleaned:  <title>My example: <strong>A text %very% $clean!!</strong></title>
                    1st clean:  My example: A text %very% $clean!!
                    2nd clean:  My example: A text very clean
                    

                    【讨论】:

                      【解决方案20】:

                      低于一个..没有使用正则表达式的概念..

                      ipstring ="text with symbols!@#$^&*( ends here"
                      opstring=''
                      for i in ipstring:
                          if i.isalnum()==1 or i==' ':
                              opstring+=i
                          pass
                      print opstring
                      

                      【讨论】:

                        【解决方案21】:

                        这个怎么样:

                        def text_cleanup(text):
                            new = ""
                            for i in text:
                                if i not in " ?.!/;:":
                                    new += i
                            return new
                        

                        【讨论】:

                        • 您能否详细说明您的答案,添加更多关于您提供的解决方案的描述?
                        • 添加到列表,然后使用连接比串联更有效
                        【解决方案22】:
                        #!/usr/bin/python
                        import re
                        
                        strs = "how^ much for{} the maple syrup? $20.99? That's[] ricidulous!!!"
                        print strs
                        nstr = re.sub(r'[?|$|.|!|a|b]',r' ',strs)#i have taken special character to remove but any #character can be added here
                        print nstr
                        nestr = re.sub(r'[^a-zA-Z0-9 ]',r'',nstr)#for removing special character
                        print nestr
                        

                        【讨论】:

                        【解决方案23】:

                        对于字符串中允许某些字符的逆要求,您可以使用带有集合补码运算符[^ABCabc]的正则表达式。例如,要删除除 ascii 字母、数字和连字符之外的所有内容:

                        >>> import string
                        >>> import re
                        >>>
                        >>> phrase = '  There were "nine" (9) chick-peas in my pocket!!!      '
                        >>> allow = string.letters + string.digits + '-'
                        >>> re.sub('[^%s]' % allow, '', phrase)
                        
                        'Therewerenine9chick-peasinmypocket'
                        

                        来自the python regular expression documentation

                        不在一个范围内的字符可以通过补码来匹配 集。如果集合的第一个字符是'^',则所有字符 不在集合中的将被匹配。例如,[^5] 将匹配 除 '5' 以外的任何字符,[^^] 将匹配除 '5' 以外的任何字符 '^'^ 如果不是第一个字符,则没有特殊含义 设置。

                        【讨论】:

                          【解决方案24】:

                          提问者几乎得到它。就像 Python 中的大多数东西一样,答案比你想象的要简单。

                          >>> line = "H E?.LL!/;O:: "  
                          >>> for char in ' ?.!/;:':  
                          ...  line = line.replace(char,'')  
                          ...
                          >>> print line
                          HELLO
                          

                          您不必执行嵌套的 if/for 循环,但您需要单独检查每个字符。

                          【讨论】:

                          • 是的,我知道,可能为时已晚,但如果你逃脱它应该可以工作。像这样: line = line.replace('`', '') 继续阅读:learnpythonthehardway.org/book/ex10.html
                          • 这可能不高效,因为您要为每个字符分配一个新字符串
                          【解决方案25】:
                          >>> line = "abc#@!?efg12;:?"
                          >>> ''.join( c for c in line if  c not in '?:!/;' )
                          'abc#@efg12'
                          

                          【讨论】:

                          • 使用另一个字符串分隔符,例如 ''' 或 "
                          • 如果你有很多被禁止的字符,你可以先把它变成一个集合来加速你的代码。 blacklist = set('?:!/;') 然后''.join(c for c in line if c not in blacklist)
                          【解决方案26】:
                          line = line.translate(None, " ?.!/;:")
                          

                          【讨论】:

                          猜你喜欢
                          • 2013-10-31
                          • 1970-01-01
                          • 2020-06-06
                          • 1970-01-01
                          • 2018-09-16
                          • 2017-04-18
                          • 2015-10-02
                          相关资源
                          最近更新 更多