Remover caracteres específicos de uma sequência em Python

546

Estou tentando remover caracteres específicos de uma string usando Python. Este é o código que estou usando agora. Infelizmente, parece não fazer nada com a string.

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

Como faço isso corretamente?

Matt Phillips
fonte
23
Tem sido ao longo de 5 anos, mas como sobre o uso da filterfunção e uma expressão lambda: filter(lambda ch: ch not in " ?.!/;:", line). Bastante conciso e eficiente também, eu acho. Obviamente, ele retorna uma nova string à qual você terá que atribuir um nome.
John Red
3
@ JohnRed: Na verdade, ele retorna um iterador que retorna uma lista de caracteres, mas se você colocar isso em uma resposta, alguns de nós ficarão satisfeitos em votar novamente.
Bill Sino
@BillBell: PS: é um iterador em Python3 e uma string, tupla ou lista em python2
serv-inc

Respostas:

626

Strings em Python são imutáveis (não podem ser alteradas). Por isso, o efeito de line.replace(...)é apenas criar uma nova string, em vez de alterar a antiga. Você precisa religá-lo (atribuí-lo) linepara que essa variável aceite o novo valor, com esses caracteres removidos.

Além disso, a maneira como você está fazendo isso será lenta, relativamente. Também é provável que seja um pouco confuso para os pythonators experientes, que verão uma estrutura duplamente aninhada e pensarão por um momento que algo mais complicado está acontecendo.

A partir do Python 2.6 e versões mais recentes do Python 2.x *, você pode usar str.translate(mas leia as diferenças do Python 3):

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

ou substituição de expressão regular com re.sub

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

Os caracteres entre colchetes constituem uma classe de caracteres . Quaisquer caracteres lineque estejam nessa classe são substituídos pelo segundo parâmetro para sub: uma sequência vazia.

No Python 3, as strings são Unicode. Você terá que traduzir um pouco diferente. O kevpie menciona isso em um comentário em uma das respostas, e está anotado na documentação destr.translate .

Ao chamar o translatemétodo de uma seqüência de caracteres Unicode, você não pode passar o segundo parâmetro que usamos acima. Você também não pode passar Nonecomo o primeiro parâmetro. Em vez disso, você passa uma tabela de tradução (geralmente um dicionário) como o único parâmetro. Esta tabela mapeia os valores ordinais dos caracteres (ou seja, o resultado de ordinvocá-los) para os valores ordinais dos caracteres que devem substituí-los, ou - útil para nós - Nonepara indicar que eles devem ser excluídos.

Então, para fazer a dança acima com uma string Unicode, você chamaria algo como

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

Aqui dict.fromkeyse mapsão usados ​​para gerar sucintamente um dicionário contendo

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

Ainda mais simples, como outra resposta coloca , crie a tabela de tradução no local:

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

Ou crie a mesma tabela de tradução com str.maketrans:

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

* para compatibilidade com Pythons anteriores, você pode criar uma tabela de tradução "nula" para substituir None:

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

Aqui string.maketransé usado para criar uma tabela de conversão , que é apenas uma sequência que contém os caracteres com valores ordinais de 0 a 255.

intuído
fonte
26
Em Python3, line.translatetem apenas um argumento e a primeira solução não vai funcionar
marczoid
33
Em python3, str.translate () não aceita o segundo argumento. Então, sua resposta será line.translate({ord(i):None for i in '!@#$'})
naveen
1
Igual a qualquer outro personagem. O Python permite usar pares de aspas simples ou duplas. Então você acabou de escrever "'"para o conjunto de caracteres.
intuited
2
O comentário de @ naveen acima funcionou para mim. Pythony 2.7.13. No meu caso, eu queria despir "e 'caracteres:notes = notes.translate({ord(i):None for i in '\"\''})
RyanG 14/03
1
No Python 3, você pode usar unicode_line.translate(str.maketrans('', '', '!@#$')). Ouunicode_line.translate(dict.fromkeys(map(ord, '!@#$')))
Martijn Pieters
234

Estou perdendo o ponto aqui, ou é apenas o seguinte:

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

print string
# result: "abcdef"

Coloque-o em um loop:

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

print a
# result: "abcd"
gsbabil
fonte
26
Isso fará uma cópia da string em cada loop, o que pode não ser desejável. Também não é muito bom Python. Em Python, você faria um loop como esse:for char in b: a=a.replace(char,"")
elgehelge
2
o uso de variáveis ​​definidas pelo usuário que se sobrepõem às classes do sistema não é uma boa ideia. É melhor usar a variável STRING em vez de STR e C em vez de CHAR.
Ayrat 26/08/16
Precisa ser em string=string.replace("1","")vez disso. Você meio que disse isso na parte do loop do seu exemplo, mas a maioria das pessoas não lerá tão longe sua resposta até que tenha mexido um pouco com o código primeiro em uma pergunta tão simples.
CodeMed 13/06/19
Uma boa solução, mas não tão Python-esk quanto uma das outras.
Steve
45
>>> line = "abc#@!?efg12;:?"
>>> ''.join( c for c in line if  c not in '?:!/;' )
'abc#@efg12'
ghostdog74
fonte
use outro delimitador de string, como '' 'ou "
ALisboa
1
Se você tiver muitos caracteres proibidos, poderá acelerar seu código transformando-o em um conjunto primeiro. blacklist = set('?:!/;')e então''.join(c for c in line if c not in blacklist)
Boris
32

Fácil e fácil com re.sub expressão regular a partir do Python 3.5

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

Exemplo

>>> import re

>>> line = 'Q: Do I write ;/.??? No!!!'

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

Explicação

Nas expressões regulares (regex), |é um OR lógico e \escapa de espaços e caracteres especiais que podem ser comandos reais de regex. Considerando que subsignifica substituição, neste caso com a cadeia vazia ''.

Serge Stroobandt
fonte
22

Para o requisito inverso de permitir apenas determinados caracteres em uma sequência, você pode usar expressões regulares com um operador de complemento definido [^ABCabc]. Por exemplo, para remover tudo, exceto letras ascii, dígitos e o hífen:

>>> 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'

Na documentação da expressão regular do python :

Os caracteres que não estão dentro de um intervalo podem ser correspondidos complementando o conjunto. Se o primeiro caractere do conjunto for '^', todos os caracteres que não estão no conjunto serão correspondidos. Por exemplo, [^5]corresponderá a qualquer caractere, exceto '5', e [^^]corresponderá a qualquer caractere, exceto '^'. ^não tem significado especial se não for o primeiro caractere do conjunto.

cod3monk3y
fonte
19

O autor da pergunta quase o pegou. Como a maioria das coisas em Python, a resposta é mais simples do que você pensa.

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

Você não precisa executar o loop aninhado if / for, mas precisa verificar cada caractere individualmente.

mgold
fonte
Sim, eu sei, provavelmente tarde demais, mas deve funcionar se você escapar. Como isso: line = line.replace ('' ',' ') continue lendo: learnpythonthehardway.org/book/ex10.html
Aiyion.Prime
Provavelmente isso não tem desempenho, porque você está alocando uma nova string para cada caractere
OneCricketeer
11
>>> s = 'a1b2c3'
>>> ''.join(c for c in s if c not in '123')
'abc'
eatkin
fonte
2
Minha resposta fornece uma solução para a pergunta original, mas eu também estava interessado (talvez também no OP) em comentários sobre por que minha solução pode não ser ideal. Eu deveria ter criado uma nova pergunta e referenciado esta para contexto?
eatkin
Isso recebe meu voto. Python conciso
Steve
9

Strings são imutáveis ​​em Python. O replacemétodo retorna uma nova sequência após a substituição. Tentar:

for char in line:
    if char in " ?.!/;:":
        line = line.replace(char,'')
Greg Hewgill
fonte
Como você pode iterar por linha e modificá-lo ao mesmo tempo?
eumiro
1
@eumiro: A iteração prossegue sobre o original line .
Greg Hewgill 15/10/10
bom saber! Portanto, se eu iterar sobre uma matriz, iterarei sobre uma matriz original. A iteração sobre um iterador não seria possível.
eumiro
9

Fiquei surpreso que ninguém ainda tivesse recomendado o uso da função de filtro embutido .

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

    s = "1212edjaq"

Digamos que queremos filtrar tudo o que não é um número. Usando o método interno do filtro "... é equivalente à expressão do gerador (item para item em iterável se função (item))" [ Python 3 Builtins: Filter ]

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

No Python 3, isso retorna

    >>  <filter object @ hex>

Para obter uma sequência impressa,

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

Não sei como filtrar é classificado em termos de eficiência, mas é bom saber como usar ao fazer a compreensão de listas e coisas do tipo.

ATUALIZAR

Logicamente, como o filtro funciona, você também pode usar a compreensão da lista e, pelo que li, deve ser mais eficiente, porque lambdas são os gerentes de fundos de hedge de wall street do mundo das funções de programação. Outra vantagem é que é uma linha que não requer nenhuma importação. Por exemplo, usando a mesma string 's' definida acima,

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

É isso aí. O retorno será uma sequência de todos os caracteres que são dígitos na sequência original.

Se você tiver uma lista específica de caracteres aceitáveis ​​/ inaceitáveis, precisará ajustar apenas a parte 'se' da compreensão da lista.

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

ou alternativamente,

      target_chars = "".join([i for i in s if i not in some_list])
Dan Temkin
fonte
Não há razão para usar operator.containsse você estiver usando um de lambdaqualquer maneira. lambda x: operator.contains(intsList, x)deve ser escrito lambda x: x in intsListou, se você estiver tentando obter a verificação do nível C, intsList.__contains__(de maneira alguma lambda) fará o truque.
ShadowRanger
8

Usando filter, você precisaria apenas de uma linha

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

Isso trata a string como iterável e verifica todos os caracteres se o lambdaretorno 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.
serv-inc
fonte
4

Aqui estão algumas maneiras possíveis de realizar esta tarefa:

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: Em vez de usar "?.! / ;:", os exemplos usam as vogais ... e sim, "murcielago" é a palavra em espanhol para dizer morcego ... palavra engraçada, pois contém todas as vogais :)

PS2: se você estiver interessado em desempenho, poderá medir essas tentativas com um código simples como:

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

Na minha caixa você teria:

attempt1 2.2334518376057244
attempt2 1.8806643818474513
attempt3 7.214925774955572
attempt4 1.7271184513757465

Parece que a tentativa4 é a mais rápida para essa entrada específica.

BPL
fonte
1
Você está criando um listin desnecessário attempt1e a tupla pode ser reescrita "aeiou"para simplificar (remover [e ]se transformará em um gerador sem criar uma lista). Você cria toneladas de strings intermediários descartáveis attemt2, usa vários aplicativos de regex nos attempt3quais pode usar r'[aeiou]'em uma única passagem. cada um tem falhas - seu bom ver diferentes maneiras de fazer as coisas, mas por favor corrigi-los para serem bons tentativas bem
Patrick Artner
1
@PatrickArtner Você está absolutamente certo ... das dezenas de maneiras que tenho em mente para realizar essa tarefa, escolhi as mais lentas (queria mostrar ao OP algumas mais fáceis) ... Dito isso, depois de você caras fecharam o outro tópico Perdi a motivação de colocar mais esforço nesse tópico já respondido, então ... :). Obrigado pelos pontos embora.
BPL 22/07
@PatrickArtner Ok ... apenas para apenas bem acrescentou um novo, "attempt4" ... não têm medido, mas eu acho que um deve ser o mais rápido um
BPL
1
@PatrickArtner Edited ... a tentativa4 foi a mais rápida do pequeno conjunto de tentativas. De qualquer forma, eu não vou perder mais tempo com este material :)
BPL
3

Aqui está minha versão compatível com Python 2/3. Desde que a API de conversão mudou.

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)
Bryce Guinta
fonte
Eu usaria dict.fromkeys(map(ord, '!@#$'))para criar o mapa.
Martijn Pieters
mapgeralmente é menos legível do que uma compreensão de lista / dict / set / generator. Tanto que Guido queria removê- lo do idioma . O uso fromkeystambém é um pouco inteligente e requer uma verificação de documento.
Bryce Guinta
1
@MartijnPieters: Para Python 3, deveria ser str.maketrans('', '', chars), que lida com a ordconversão e a dictconstrução de uma só vez (sem mencionar que é bastante mais óbvio na intenção, pois foi projetado para emparelhar str.translate).
ShadowRanger
1
#!/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
pkm
fonte
Você quer dizer marcas de fala? re tem barra invertida para escapar do código e considerar 'como uma sequência. docs.python.org/2/library/re.html
JasTonAChair 5/05
1

Que tal agora:

def text_cleanup(text):
    new = ""
    for i in text:
        if i not in " ?.!/;:":
            new += i
    return new
Wariat
fonte
1
Você poderia elaborar mais sua resposta adicionando um pouco mais de descrição sobre a solução que você fornece?
abarisone
Somando-se a uma lista, em seguida, usando juntar-se seria mais eficiente do que a concatenação
OneCricketeer
1

Você também pode usar uma função para substituir diferentes tipos de expressão regular ou outro padrão pelo uso de uma lista. Com isso, você pode misturar expressões regulares, classe de caracteres e padrão de texto realmente básico. É realmente útil quando você precisa substituir muitos elementos, como os HTML.

* NB: funciona com 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)

Na função string_cleanup, sua string x e ​​sua lista não são desejadas como argumentos. Para cada item nessa lista de elementos ou padrão, se for necessário um substituto, isso será feito.

A saída:

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
Djidiouf
fonte
1

Meu método que eu usaria provavelmente não funcionaria tão eficientemente, mas é extremamente simples. Posso remover vários caracteres em diferentes posições ao mesmo tempo, usando fatias e formatação. Aqui está um exemplo:

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

Isso resultará em 'removido' mantendo a palavra 'isto'.

A formatação pode ser muito útil para imprimir variáveis ​​no meio de uma sequência de impressão. Ele pode inserir qualquer tipo de dados usando um % seguido pelo tipo de dados da variável; todos os tipos de dados podem usar % se flutuadores (também conhecidos como decimais) e números inteiros podem usar % d .

O fatiamento pode ser usado para um controle intrincado sobre as strings. Quando coloco as palavras [: 3] , ele permite selecionar todos os caracteres da sequência desde o início (os dois pontos estão antes do número, isso significa 'do começo para') até o 4º caractere (inclui o 4º personagem). O motivo 3 é igual à 4ª posição é porque o Python começa em 0. Então, quando eu coloco a palavra [-1:] , significa o segundo último caractere até o fim (os dois pontos estão atrás do número). Colocar -1 fará com que o Python conte desde o último caractere, e não o primeiro. Novamente, o Python começará em 0. Portanto, a palavra [-1:] basicamente significa 'do segundo último caractere até o final da string.

Portanto, cortando os caracteres antes do personagem que eu quero remover e depois e colocando-os juntos, posso remover o personagem indesejado. Pense nisso como uma salsicha. No meio está sujo, então eu quero me livrar dele. Simplesmente corto as duas pontas que quero e depois as uno sem a parte indesejada no meio.

Se eu quiser remover vários caracteres consecutivos, basta mudar os números no [] (parte de fatiar). Ou, se quiser remover vários caracteres de diferentes posições, posso simplesmente colocar várias fatias de uma só vez.

Exemplos:

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

removido é igual a 'legal'.

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

removido é igual a 'macs'.

Nesse caso, [3: 5] significa caractere na posição 3 a caractere na posição 5 (excluindo o caractere na posição final).

Lembre-se, o Python começa a contar em 0 , então você também precisará.

oisinvg
fonte
0

Tente este:

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

Este método funciona bem no python 3.5.2

Joseph Lee
fonte
0

Você pode usar a substituição da expressão regular do módulo re. Usar a expressão ^ permite escolher exatamente o que você deseja da sua string.

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

A saída para isso seria "Thisisabsurd". Somente as coisas especificadas após o símbolo ^ aparecerão.

Shreyas Rajesh
fonte
0

O método string replacenão modifica a string original. Deixa o original sozinho e retorna uma cópia modificada.

O que você quer é algo como: line = line.replace(char,'')

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

No entanto, a criação de uma nova sequência toda vez que um caractere é removido é muito ineficiente. Eu recomendo o seguinte:

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
Samuel Muldoon
fonte
-1

Abaixo um .. sem usar o conceito de expressão regular ..

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

No Python 3.5

por exemplo,

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

Para remover todo o número da string

BonieSV
fonte
-1

você pode usar set

    charlist = list(set(string.digits+string.ascii_uppercase) - set('10IO'))
    return ''.join([random.SystemRandom().choice(charlist) for _ in range(passlen)])
Xu Zhenlei
fonte
Ao dar uma resposta, é preferível dar uma explicação sobre POR QUE sua resposta é essa.
Stephen Rauch
-1

Divisão recursiva: s = string; chars = caracteres a serem removidos

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)

exemplo:

print(strip("Hello!","lo"))    #He!
mate
fonte
-1

# para cada arquivo em um diretório, renomeie o nome do arquivo

   file_list = os.listdir (r"D:\Dev\Python")

   for file_name in file_list:

       os.rename(file_name, re.sub(r'\d+','',file_name))
Robert Silva
fonte
-1

Mesmo a abordagem abaixo funciona

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

resultado: abcde

M2skills
fonte
-2
>>> # Character stripping
>>> a = '?abcd1234!!'
>>> t.lstrip('?')
'abcd1234!!'
>>> t.strip('?!')
'abcd1234'
Arihant Bansal
fonte
10
Apenas remove caracteres do início ou final da cadeia
divenex