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

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

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


当前回答

line = line.translate(None, " ?.!/;:")

其他回答

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

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

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

注意,字符串replace()方法会替换字符串中出现的所有字符,因此可以对想要删除的每个字符使用replace(),而不是遍历字符串中的每个字符,这样做会更好。

Python中的字符串是不可变的(不能更改)。因此,line.replace(…)的作用只是创建一个新字符串,而不是更改旧字符串。您需要将其重新绑定(赋值)到行,以便使该变量具有新的值,并删除那些字符。

而且,你做的方法相对来说会比较慢。对于有经验的python编程者来说,这也可能会有点困惑,他们会看到一个双嵌套结构,并认为正在发生更复杂的事情。

从Python 2.6和更新的Python 2开始。x版本*,你可以使用str.translate,(见下面的Python 3答案):

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

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

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

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

Python 3答案

在Python 3中,字符串是Unicode。你得翻译得有点不同。Kevpie在其中一个答案的注释中提到了这一点,str.translate的文档中也提到了这一点。

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

因此,要对Unicode字符串执行上述操作,您可以调用类似于

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

这里使用dict.fromkeys和map简洁地生成包含

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

甚至更简单,正如另一个答案所说,在适当的地方创建翻译表:

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

或者,正如Joseph Lee提出的那样,使用str.maketrans创建相同的翻译表:

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

*为了与早期的python兼容,你可以创建一个“null”转换表来代替None:

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

这里的字符串。Maketrans用于创建一个转换表,它只是一个包含序数为0到255的字符的字符串。

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

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

附注:在使用" ?.!/;:"的例子中使用元音…是的,“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似乎是最快的。

令我惊讶的是,还没有人推荐使用内置的过滤功能。

    import operator
    import string # only for the example you could use a custom string

    s = "1212edjaq"

假设我们想过滤掉所有不是数字的东西。使用过滤器内置方法“…等效于生成器表达式(item for item在可迭代if函数(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"

我不确定过滤器在效率方面的排名,但在做列表理解等时,知道如何使用是一件好事。

更新

从逻辑上讲,既然过滤器可以工作,你也可以使用列表理解,从我所读到的,它应该更有效,因为lambdas是编程函数世界的华尔街对冲基金经理。另一个优点是它是一个单行程序,不需要任何导入。例如,使用上面定义的字符串'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])

你可以使用re模块的正则表达式替换。使用^表达式可以准确地从字符串中选择想要的内容。

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

输出结果将是“这是荒谬的”。只有在^符号之后指定的内容才会出现。