Eliminar caracteres específicos de una cadena en Python


548

Estoy tratando de eliminar caracteres específicos de una cadena usando Python. Este es el código que estoy usando en este momento. Desafortunadamente, parece no hacer nada a la cadena.

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

¿Cómo hago esto correctamente?


23
Han pasado más de 5 años, pero ¿cómo sobre el uso de la filterfunción y una expresión lambda: filter(lambda ch: ch not in " ?.!/;:", line). Bastante conciso y eficiente también, creo. Por supuesto, devuelve una nueva cadena a la que deberá asignar un nombre.
John Red

3
@JohnRed: En realidad, devuelve un iterador que devuelve una lista de caracteres, pero si usted pone esto en una respuesta, algunos de nosotros estaríamos contentos de votarlo.
Bill Bell


@BillBell: PD: es un iterador en Python3 y una cadena, tupla o lista en Python2
serv-inc

Respuestas:


628

Las cadenas en Python son inmutables (no se pueden cambiar). Debido a esto, el efecto de line.replace(...)es simplemente crear una nueva cadena, en lugar de cambiar la antigua. Debe volver a vincularlo (asignarlo) linepara que esa variable tome el nuevo valor, con esos caracteres eliminados.

Además, la forma en que lo haga será un poco lenta, relativamente. También es probable que sea un poco confuso para los pitonadores experimentados, que verán una estructura doblemente anidada y pensarán por un momento que algo más complicado está sucediendo.

A partir de Python 2.6 y las versiones más recientes de Python 2.x *, puede usar str.translate, (pero siga leyendo para conocer las diferencias de Python 3):

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

o reemplazo de expresión regular con re.sub

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

Los caracteres entre paréntesis constituyen una clase de caracteres . Cualquier carácter en el lineque se encuentre en esa clase se reemplaza con el segundo parámetro para sub: una cadena vacía.

En Python 3, las cadenas son Unicode. Tendrás que traducir un poco diferente. kevpie menciona esto en un comentario sobre una de las respuestas, y está anotado en la documentación destr.translate .

Al llamar al translatemétodo de una cadena Unicode, no puede pasar el segundo parámetro que usamos anteriormente. Tampoco puede pasar Nonecomo primer parámetro. En cambio, pasa una tabla de traducción (generalmente un diccionario) como el único parámetro. Esta tabla asigna los valores ordinales de los caracteres (es decir, el resultado de invocarlos ord) a los valores ordinales de los caracteres que deberían reemplazarlos o, útilmente para nosotros, Nonepara indicar que deberían eliminarse.

Entonces, para hacer el baile anterior con una cuerda Unicode, llamarías algo así como

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

Aquí dict.fromkeysy mapse utilizan para generar sucintamente un diccionario que contiene

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

Aún más simple, como dice otra respuesta , cree la tabla de traducción en su lugar:

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

O cree la misma tabla de traducción con str.maketrans:

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

* para compatibilidad con Python anteriores, puede crear una tabla de traducción "nula" para pasarla en lugar de None:

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

Aquí string.maketransse utiliza para crear una tabla de traducción , que es solo una cadena que contiene los caracteres con valores ordinales de 0 a 255.


26
En Python3, line.translatetoma solo un argumento y la primera solución no funcionará
marczoid

33
En python3, str.translate () no toma el segundo argumento. Entonces, su respuesta será line.translate({ord(i):None for i in '!@#$'})
naveen

1
Igual que cualquier otro personaje. Python le permite usar pares de comillas simples o dobles. Entonces solo escribes "'"para el conjunto de caracteres.
intuido

2
El comentario de @ naveen anterior funcionó para mí. Pythony 2.7.13. En mi caso, quería desnudar "y 'personajes:notes = notes.translate({ord(i):None for i in '\"\''})
RyanG

1
En Python 3, puedes usar unicode_line.translate(str.maketrans('', '', '!@#$')). Ounicode_line.translate(dict.fromkeys(map(ord, '!@#$')))
Martijn Pieters

234

¿Me estoy perdiendo el punto aquí, o es solo lo siguiente:

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

print string
# result: "abcdef"

Ponlo en un bucle:

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

print a
# result: "abcd"

26
Esto hará una copia de la cadena en cada bucle, lo que podría no ser deseable. Además, no es muy bueno Python. En Python, deberías hacer un bucle como este:for char in b: a=a.replace(char,"")
elgehelge

2
No es una buena idea utilizar variables definidas por el usuario que se superpongan con las clases del sistema. Será mejor que use la variable STRING en lugar de STR y C en lugar de CHAR.
Ayrat

Necesita ser string=string.replace("1","")en su lugar. Has dicho esto en la parte del bucle de tu ejemplo, pero la mayoría de las personas no leerán tanto en tu respuesta hasta que hayan tocado el código un poco primero para una pregunta tan simple.
CodeMed

Una buena solución pero no tan Python-esk como una de las otras.
Steve

45
>>> line = "abc#@!?efg12;:?"
>>> ''.join( c for c in line if  c not in '?:!/;' )
'abc#@efg12'

use otro delimitador de cadena como '' 'o "
ALisboa

1
Si tiene muchos caracteres prohibidos, puede acelerar su código convirtiéndolo primero en un conjunto. blacklist = set('?:!/;')y luego''.join(c for c in line if c not in blacklist)
Boris

32

Fácil guisante con re.subexpresión regular a partir de Python 3.5

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

Ejemplo

>>> import re

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

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

Explicación

En expresiones regulares (regex), |es un OR lógico y \escapa a espacios y caracteres especiales que podrían ser comandos regex reales. Mientras que subsignifica sustitución, en este caso con la cadena vacía ''.


22

Para el requisito inverso de permitir solo ciertos caracteres en una cadena, puede usar expresiones regulares con un operador de complemento establecido [^ABCabc]. Por ejemplo, para eliminar todo excepto las letras ascii, los dígitos y el guión:

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

De la documentación de expresiones regulares de Python :

Los caracteres que no están dentro de un rango pueden coincidir complementando el conjunto. Si el primer carácter del conjunto es '^', todos los caracteres que no están en el conjunto serán emparejados. Por ejemplo, [^5]coincidirá con cualquier carácter excepto '5', y [^^]coincidirá con cualquier carácter excepto '^'. ^no tiene un significado especial si no es el primer personaje del conjunto.


19

El autor de la pregunta casi lo tuvo. Como la mayoría de las cosas en Python, la respuesta es más simple de lo que piensas.

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

No tiene que hacer el bucle anidado if / for, pero SÍ debe verificar cada carácter individualmente.


Sí, lo sé, probablemente demasiado tarde, pero debería funcionar si escapas. Así: line = line.replace ('' ',' ') sigue
Aiyion.Prime

Esto probablemente no sea
eficaz


11
>>> s = 'a1b2c3'
>>> ''.join(c for c in s if c not in '123')
'abc'

2
Mi respuesta proporciona una solución a la pregunta original, pero también estaba interesado (y quizás también el OP) en recibir comentarios sobre por qué mi solución podría no ser la ideal. ¿Debería haber creado una nueva pregunta y hacer referencia a esta para el contexto?
eatkin

Esto consigue mi voto. Python conciso
Steve

9

Las cadenas son inmutables en Python. El replacemétodo devuelve una nueva cadena después del reemplazo. Tratar:

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

¿Cómo puede iterar sobre la línea y modificarla al mismo tiempo?
eumiro

1
@eumiro: la iteración continúa sobre el original line .
Greg Hewgill

¡bueno saber! Entonces, si itero sobre una matriz, itero sobre una matriz original. La iteración sobre un iterador no sería posible.
eumiro

9

Me sorprendió que nadie hubiera recomendado todavía usar la función de filtro incorporado .

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

    s = "1212edjaq"

Digamos que queremos filtrar todo lo que no sea un número. Usar el método de filtro incorporado "... es equivalente a la expresión del generador (elemento para elemento en iterable si función (elemento))" [ Python 3 Builtins: Filter ]

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

En Python 3 esto vuelve

    >>  <filter object @ hex>

Para obtener una cadena impresa,

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

No estoy seguro de cómo filtrar clasifica el en términos de eficiencia, pero es bueno saber cómo usarlo al hacer listas de comprensión y demás.

ACTUALIZAR

Lógicamente, dado que el filtro funciona, también podría usar la comprensión de la lista y, por lo que he leído, se supone que es más eficiente porque las lambdas son los administradores de fondos de cobertura de Wall Street del mundo de la función de programación. Otra ventaja es que es un trazador de líneas que no requiere ninguna importación. Por ejemplo, usando la misma cadena 's' definida anteriormente,

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

Eso es. El retorno será una cadena de todos los caracteres que son dígitos en la cadena original.

Si tiene una lista específica de caracteres aceptables / inaceptables, solo necesita ajustar la parte 'si' de la comprensión de la lista.

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

o alternativamente,

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

No hay razón para usar operator.containssi está usando un de lambdatodos modos. lambda x: operator.contains(intsList, x)debe deletrearse lambda x: x in intsList, o si está tratando de obtener la verificación de nivel C, intsList.__contains__(no, lambdaen absoluto) hará el truco.
ShadowRanger

8

Usando filter, solo necesitarías una línea

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

Esto trata la cadena como un iterable y verifica cada carácter si lambdadevuelve 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.

4

Aquí hay algunas formas posibles de lograr esta tarea:

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

PD: En lugar de usar "?.! / ;:" los ejemplos usan las vocales ... y sí, "murcielago" es la palabra en español que dice murciélago ... palabra divertida ya que contiene todas las vocales :)

PS2: si está interesado en el rendimiento, puede medir estos intentos con un código simple 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))

En mi caja obtendrías:

attempt1 2.2334518376057244
attempt2 1.8806643818474513
attempt3 7.214925774955572
attempt4 1.7271184513757465

Entonces parece que intent4 es el más rápido para esta entrada en particular.


1
Está creando una innecesaria listen attempt1y la tupla puede reescribirse a "aeiou"por el bien de la simplicidad (eliminación [y ]se convertirá en en un generador sin crear una lista). Crea toneladas de cadenas intermedias desechables attemt2, usa múltiples aplicaciones de expresiones regulares en las attempt3que podría usar r'[aeiou]'en una sola pasada. cada uno tiene defectos, es agradable ver diferentes formas de hacer las cosas, pero por favor, corríjalos como buenos intentos también
Patrick Artner

1
@PatrickArtner Tienes toda la razón ... de las docenas de formas que tengo en mente para lograr esta tarea, he elegido las más lentas (quería mostrarle al OP algunas de las más fáciles) ... Dicho esto, después de ti chicos cerraron el otro hilo He perdido la motivación para poner más esfuerzo en este hilo ya respondido, así que ... :). Gracias por los puntos sin embargo.
BPL

@PatrickArtner Ok ... solo por el bien, agregué uno nuevo, "intent4" ... no lo he medido, pero creo que debería ser el más rápido
BPL

1
@PatrickArtner Edited ... intent4 fue el más rápido del pequeño conjunto de intentos. De todos modos, no estoy perdiendo más tiempo con estas cosas :)
BPL

3

Aquí está mi versión compatible con Python 2/3. Desde la API de traducción ha cambiado.

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)

Lo usaría dict.fromkeys(map(ord, '!@#$'))para crear el mapa.
Martijn Pieters

mapgeneralmente es menos legible que una lista / dict / set / generador de comprensión. Tanto es así que Guido quería eliminarlo del idioma . El uso fromkeystambién es un poco inteligente y requiere una verificación de documentos.
Bryce Guinta

1
@MartijnPieters: para Python 3, debería ser str.maketrans('', '', chars), que maneja la ordconversión y la dictconstrucción de una sola vez (sin mencionar que es bastante más obvio en su intención, ya que está diseñado para emparejarse 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

¿Te refieres a marcas de voz? re tiene una barra invertida para escapar del código y considerarlo 'como una cadena. docs.python.org/2/library/re.html
JasTonAChair

1

Qué tal esto:

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

1
¿Podría elaborar más su respuesta agregando un poco más de descripción sobre la solución que proporciona?
abarisone

Agregar a una lista, luego usar join sería más eficiente que la concatenación
OneCricketeer

1

También puede usar una función para sustituir diferentes tipos de expresión regular u otro patrón con el uso de una lista. Con eso, puedes mezclar expresiones regulares, clases de caracteres y patrones de texto realmente básicos. Es realmente útil cuando necesita sustituir muchos elementos como los HTML.

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

En la función string_cleanup, toma su cadena xy su lista no deseada como argumentos. Para cada elemento de esa lista de elementos o patrón, si se necesita un sustituto, se realizará.

La salida:

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

1

Mi método que usaría probablemente no funcionaría tan eficientemente, pero es enormemente simple. Puedo eliminar varios caracteres en diferentes posiciones a la vez, usando el corte y el formateo. Aquí hay un ejemplo:

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

Esto dará como resultado 'eliminado' que contenga la palabra 'esto'.

El formateo puede ser muy útil para imprimir variables a la mitad de una cadena de impresión. Puede insertar cualquier tipo de datos usando un % seguido del tipo de datos de la variable; todos los tipos de datos pueden usar % s , y los flotantes (también conocidos como decimales) y los enteros pueden usar % d .

El corte se puede usar para un control complejo sobre las cadenas. Cuando pongo palabras [: 3] , me permite seleccionar todos los caracteres de la cadena desde el principio (los dos puntos están antes del número, esto significará 'desde el principio hasta') hasta el cuarto carácter (incluye el cuarto personaje). La razón por la que 3 es igual hasta la cuarta posición es porque Python comienza en 0. Luego, cuando pongo la palabra [-1:] , significa el segundo último carácter hasta el final (los dos puntos están detrás del número). Poner -1 hará que Python cuente desde el último personaje, en lugar del primero. Nuevamente, Python comenzará en 0. Entonces, la palabra [-1:] básicamente significa 'desde el segundo último carácter hasta el final de la cadena.

Entonces, cortando los caracteres antes del carácter que quiero eliminar y los caracteres después y emparejándolos, puedo eliminar el carácter no deseado. Piensa en ello como una salchicha. En el medio está sucio, así que quiero deshacerme de él. Simplemente corté los dos extremos que quiero y luego los uní sin la parte no deseada en el medio.

Si quiero eliminar varios caracteres consecutivos, simplemente cambio los números en [] (parte de división). O si quiero eliminar varios caracteres de diferentes posiciones, simplemente puedo emparejar varias rebanadas a la vez.

Ejemplos:

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

eliminado es igual a 'genial'.

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

eliminado es igual a 'macs'.

En este caso, [3: 5] significa carácter en la posición 3 a través del carácter en la posición 5 (excluyendo el carácter en la posición final).

Recuerde, Python comienza a contar en 0 , por lo que también deberá hacerlo.


0

Prueba 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 bien en python 3.5.2


0

Puede usar el reemplazo de expresión regular del módulo re. El uso de la expresión ^ le permite elegir exactamente lo que desea de su cadena.

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

La salida a esto sería "Thisisabsurd". Solo aparecerán las cosas especificadas después del símbolo ^.


0

El método de cadena replaceno modifica la cadena original. Deja el original solo y devuelve una copia modificada.

Lo que quieres es algo como: line = line.replace(char,'')

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

Sin embargo, crear una nueva cadena cada vez que se elimina un carácter es muy ineficiente. Recomiendo lo siguiente en su lugar:

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

-1

Debajo de uno ... sin usar el concepto de expresión regular ...

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

-1

En Python 3.5

p.ej,

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

Para eliminar todo el número de la cadena



-1

División recursiva: s = cadena; caracteres = caracteres para eliminar

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)

ejemplo:

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

-1

# para cada archivo en un directorio, cambie el nombre del archivo

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

   for file_name in file_list:

       os.rename(file_name, re.sub(r'\d+','',file_name))

-1

Incluso el siguiente enfoque funciona

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

salida: abcde


-2
>>> # Character stripping
>>> a = '?abcd1234!!'
>>> t.lstrip('?')
'abcd1234!!'
>>> t.strip('?!')
'abcd1234'

10
Solo elimina caracteres del principio o del final de la cadena
divenex
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.