Supprimer des caractères spécifiques d'une chaîne en Python


548

J'essaie de supprimer des caractères spécifiques d'une chaîne à l'aide de Python. C'est le code que j'utilise en ce moment. Malheureusement, il semble ne rien faire à la chaîne.

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

Comment dois-je procéder correctement?


23
Cela fait plus de 5 ans, mais que diriez - vous à l' aide de la filterfonction et une expression Lambda: filter(lambda ch: ch not in " ?.!/;:", line). Assez concis et efficace aussi, je pense. Bien sûr, il renvoie une nouvelle chaîne à laquelle vous devrez attribuer un nom.
John Red

3
@JohnRed: En fait, il renvoie un itérateur qui renvoie une liste de caractères, mais si vous mettiez cela dans une réponse, certains d'entre nous seraient ravis de le voter.
Bill Bell


@BillBell: PS: c'est un itérateur en Python3 et une chaîne, un tuple ou une liste en Python2
serv-inc

Réponses:


628

Les chaînes en Python sont immuables (ne peuvent pas être modifiées). Pour cette raison, l'effet de line.replace(...)est simplement de créer une nouvelle chaîne, plutôt que de changer l'ancienne. Vous devez la relier (l'assigner) linepour que cette variable prenne la nouvelle valeur, avec ces caractères supprimés.

De plus, la façon dont vous le faites va être relativement lente. Cela risque également d'être un peu déroutant pour les pythonateurs expérimentés, qui verront une structure doublement imbriquée et penseront un instant que quelque chose de plus compliqué se passe.

À partir de Python 2.6 et des versions plus récentes de Python 2.x *, vous pouvez utiliser à la place str.translate, (mais lisez la suite pour les différences Python 3):

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

ou remplacement d'expression régulière par re.sub

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

Les caractères entre crochets constituent une classe de caractères . Tous les caractères dans linelesquels se trouvent dans cette classe sont remplacés par le deuxième paramètre pour sub: une chaîne vide.

En Python 3, les chaînes sont Unicode. Vous devrez traduire un peu différemment. kevpie le mentionne dans un commentaire sur l'une des réponses, et c'est noté dans la documentation destr.translate .

Lors de l'appel de la translateméthode d'une chaîne Unicode, vous ne pouvez pas passer le deuxième paramètre que nous avons utilisé ci-dessus. Vous ne pouvez pas non plus passer Nonecomme premier paramètre. Au lieu de cela, vous passez une table de traduction (généralement un dictionnaire) comme seul paramètre. Ce tableau mappe les valeurs ordinales des caractères (c'est-à-dire le résultat de leur appel ord) aux valeurs ordinales des caractères qui devraient les remplacer, ou - utilement pour nous - Nonepour indiquer qu'ils doivent être supprimés.

Donc, pour faire la danse ci-dessus avec une chaîne Unicode, vous appelleriez quelque chose comme

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

Ici dict.fromkeyset mapsont utilisés pour générer succinctement un dictionnaire contenant

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

Encore plus simple, comme le dit une autre réponse , créez la table de traduction en place:

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

Ou créez la même table de traduction avec str.maketrans:

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

* pour la compatibilité avec les Pythons antérieurs, vous pouvez créer une table de traduction "null" à passer à la place de None:

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

Ici string.maketransest utilisé pour créer une table de traduction , qui est juste une chaîne contenant les caractères avec des valeurs ordinales de 0 à 255.


26
En Python3, line.translatene prend qu'un seul argument et la première solution ne fonctionnera pas
marczoid

33
En python3, str.translate () ne prend pas le 2ème argument. Donc, votre réponse deviendra line.translate({ord(i):None for i in '!@#$'})
naveen

1
Identique à tout autre personnage. Python vous permet d'utiliser des paires de guillemets simples ou doubles. Il vous suffit donc d'écrire "'"pour le jeu de caractères.
intuition

2
Le commentaire de @ naveen ci-dessus a fonctionné pour moi. Pythony 2.7.13. Dans mon cas, je voulais notes = notes.translate({ord(i):None for i in '\"\''})
déshabiller

1
En Python 3, vous pouvez utiliser unicode_line.translate(str.maketrans('', '', '!@#$')). Ouunicode_line.translate(dict.fromkeys(map(ord, '!@#$')))
Martijn Pieters

234

Est-ce que je manque le point ici, ou est-ce juste le suivant:

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

print string
# result: "abcdef"

Mettez-le en boucle:

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

print a
# result: "abcd"

26
Cela fera une copie de la chaîne dans chaque boucle, ce qui pourrait ne pas être souhaitable. Ce n'est pas non plus un très bon Python. En Python, vous for char in b: a=a.replace(char,"")
boucleriez

2
l'utilisation de variables définies par l'utilisateur qui chevauchent les classes système n'est pas une bonne idée. Vous feriez mieux d'utiliser la variable STRING au lieu de STR et C au lieu de CHAR.
Ayrat

Doit être à la string=string.replace("1","")place. Vous avez en quelque sorte dit cela dans la partie en boucle de votre exemple, mais la plupart des gens ne liront pas aussi loin dans votre réponse qu'après avoir manipulé le code un peu en premier pour une question aussi simple.
CodeMed

Une bonne solution mais pas aussi Python-esk que l'une des autres.
Steve

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

utiliser un autre délimiteur de chaîne tel que '' 'ou "
ALisboa

1
Si vous avez beaucoup de caractères interdits, vous pouvez accélérer votre code en le transformant d'abord en un ensemble. blacklist = set('?:!/;')puis''.join(c for c in line if c not in blacklist)
Boris

32

Peasy facile avec re.subexpression régulière à partir de Python 3.5

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

Exemple

>>> import re

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

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

Explication

Dans les expressions régulières (regex), |est un OU logique et \échappe les espaces et les caractères spéciaux qui peuvent être des commandes regex réelles. Alors que subsignifie substitution, dans ce cas avec la chaîne vide ''.


22

Pour l'exigence inverse d' autoriser uniquement certains caractères dans une chaîne, vous pouvez utiliser des expressions régulières avec un opérateur complément complément [^ABCabc]. Par exemple, pour supprimer tout sauf les lettres ascii, les chiffres et le tiret:

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

Dans la documentation des expressions régulières python :

Les caractères qui ne se trouvent pas dans une plage peuvent être mis en correspondance en complétant l'ensemble. Si le premier caractère de l'ensemble est '^', tous les caractères qui ne sont pas dans l'ensemble seront mis en correspondance. Par exemple, [^5]correspondra à n'importe quel caractère sauf «5» et [^^]correspondra à n'importe quel caractère sauf '^'. ^n'a pas de signification particulière si ce n'est pas le premier caractère de l'ensemble.


19

Le demandeur l'a presque eu. Comme la plupart des choses en Python, la réponse est plus simple que vous ne le pensez.

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

Vous n'avez pas à faire la boucle imbriquée if / for, mais vous devez vérifier chaque caractère individuellement.


oui je sais, probablement trop tard, mais ça devrait marcher si vous y échappez. Comme ceci: line = line.replace ('' ',' ') lire la suite: learnpythonthehardway.org/book/ex10.html
Aiyion.Prime

Ce n'est probablement pas performant car vous
allouez


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

2
Ma réponse fournit une solution à la question d'origine, mais j'étais également intéressé (peut-être aussi par l'OP) à savoir pourquoi ma solution n'était peut-être pas idéale. Dois-je avoir créé une nouvelle question et référencé celle-ci pour le contexte?
eatkin

Cela obtient mon vote. Python concis
Steve

9

Les chaînes sont immuables en Python. La replaceméthode renvoie une nouvelle chaîne après le remplacement. Essayer:

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

Comment pouvez-vous parcourir une ligne et la modifier en même temps?
eumiro

1
@eumiro: l'itération se poursuit sur l' original line .
Greg Hewgill

bon à savoir! Donc, si j'itère sur un tableau, j'itère sur un tableau d'origine. L'itération sur un itérateur ne serait pas possible.
eumiro

9

J'ai été surpris que personne n'ait encore recommandé d'utiliser la fonction de filtre intégré .

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

    s = "1212edjaq"

Disons que nous voulons filtrer tout ce qui n'est pas un nombre. L'utilisation de la méthode de filtrage intégrée "... est équivalente à l'expression du générateur (élément pour élément dans la fonction itérable si élément (élément))" [ Python 3 Builtins: Filter ]

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

En Python 3, cela renvoie

    >>  <filter object @ hex>

Pour obtenir une chaîne imprimée,

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

Je ne sais pas comment le filtre se classe en termes d'efficacité, mais c'est une bonne chose de savoir comment l'utiliser lors de la compréhension de listes et autres.

MISE À JOUR

Logiquement, puisque le filtre fonctionne, vous pouvez également utiliser la compréhension de liste et d'après ce que j'ai lu, il est censé être plus efficace car les lambdas sont les gestionnaires de fonds spéculatifs de Wall Street du monde de la fonction de programmation. Un autre avantage est qu'il s'agit d'un monoplace qui ne nécessite aucune importation. Par exemple, en utilisant la même chaîne «s» définie ci-dessus,

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

C'est ça. Le retour sera une chaîne de tous les caractères qui sont des chiffres de la chaîne d'origine.

Si vous avez une liste spécifique de caractères acceptables / inacceptables, il vous suffit d'ajuster la partie «si» de la compréhension de la liste.

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

Ou bien,

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

Il n'y a aucune raison d'utiliser operator.containssi vous utilisez de lambdatoute façon un . lambda x: operator.contains(intsList, x)devrait être orthographié lambda x: x in intsList, ou si vous essayez d'obtenir le contrôle de niveau C, intsList.__contains__(pas lambdadu tout) fera l'affaire.
ShadowRanger

8

À l'aide filter, vous auriez juste besoin d'une ligne

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

Cela traite la chaîne comme un itérable et vérifie chaque caractère si le lambdaretourne 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

Voici quelques façons possibles d'accomplir cette tâche:

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: Au lieu d'utiliser "?.! /;:" Les exemples utilisent les voyelles ... et oui, "murcielago" est le mot espagnol pour dire bat ... mot drôle car il contient toutes les voyelles :)

PS2: Si vous êtes intéressé par les performances, vous pouvez mesurer ces tentatives avec un code simple comme:

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

Dans ma boîte, vous obtiendrez:

attempt1 2.2334518376057244
attempt2 1.8806643818474513
attempt3 7.214925774955572
attempt4 1.7271184513757465

Il semble donc que try4 soit le plus rapide pour cette entrée particulière.


1
Vous créez un listin inutile attempt1et le tuple peut être réécrit pour "aeiou"des raisons de simplicité (le supprimer [et ]le transformer en générateur sans créer de liste). Vous créez des tonnes de chaînes intermédiaires jetables dans attemt2, vous utilisez plusieurs applications de regex attempt3où vous pouvez les utiliser r'[aeiou]'en une seule passe. chacun a des défauts - c'est agréable de voir différentes façons de faire les choses, mais corrigez-les aussi pour être de bonnes tentatives
Patrick Artner

1
@PatrickArtner Vous avez tout à fait raison ... parmi les dizaines de façons que j'ai en tête pour accomplir cette tâche, j'ai choisi les plus lentes (je voulais montrer à l'OP les plus faciles) ... Cela dit, après vous les gars ont fermé l'autre thread, j'ai perdu la motivation de mettre plus d'effort sur ce vieux thread déjà répondu, alors ... :). Merci pour les points.
BPL

@PatrickArtner Ok ... juste pour le plaisir, j'en ai ajouté un nouveau, "try4" ... n'a pas mesuré mais je pense que celui-ci devrait être le plus rapide
BPL

1
@PatrickArtner Edited ... tentative4 était la plus rapide du petit ensemble de tentatives. Quoi qu'il en soit, je ne perds pas plus de temps avec ce genre de choses :)
BPL

3

Voici ma version compatible Python 2/3. Depuis l'api de traduction a changé.

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)

J'utiliserais dict.fromkeys(map(ord, '!@#$'))pour créer la carte.
Martijn Pieters

mapest généralement moins lisible qu'une compréhension de liste / dict / set / générateur. À tel point que Guido a voulu le supprimer de la langue . L'utilisation fromkeysest également un peu intelligente et nécessite une vérification de la documentation.
Bryce Guinta

1
@MartijnPieters: Pour Python 3, ce devrait être le cas str.maketrans('', '', chars), qui gère la ordconversion et la dictconstruction en une seule fois (sans parler d'être plutôt plus évident dans son intention, car il est conçu pour être couplé avec 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

Voulez-vous dire des marques de discours? re a une barre oblique inverse pour échapper au code et considérer 'comme une chaîne. docs.python.org/2/library/re.html
JasTonAChair

1

Que dis-tu de ça:

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

1
Pourriez-vous élaborer davantage votre réponse en ajoutant un peu plus de description sur la solution que vous proposez?
abarisone

Ajouter à une liste, puis utiliser la jointure serait plus efficace que la concaténation
OneCricketeer

1

Vous pouvez également utiliser une fonction afin de remplacer différents types d'expressions régulières ou d'autres modèles par l'utilisation d'une liste. Avec cela, vous pouvez mélanger expression régulière, classe de caractères et modèle de texte vraiment basique. C'est vraiment utile lorsque vous devez remplacer de nombreux éléments comme ceux HTML.

* NB: fonctionne avec 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)

Dans la fonction string_cleanup, il prend votre chaîne x et votre liste non souhaitées comme arguments. Pour chaque élément de cette liste d'éléments ou de motif, si un substitut est nécessaire, il sera effectué.

Le résultat:

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

Ma méthode que j'utiliserais ne fonctionnerait probablement pas aussi efficacement, mais elle est extrêmement simple. Je peux supprimer plusieurs caractères à différentes positions à la fois, en utilisant le découpage et le formatage. Voici un exemple:

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

Cela se traduira par «supprimé» contenant le mot «ceci».

Le formatage peut être très utile pour imprimer des variables au milieu d'une chaîne d'impression. Il peut insérer n'importe quel type de données en utilisant un % suivi du type de données de la variable; tous les types de données peuvent utiliser % s , et les flottants (aussi appelés décimales) et les entiers peuvent utiliser % d .

Le découpage peut être utilisé pour un contrôle complexe des chaînes. Lorsque je mets des mots [: 3] , cela me permet de sélectionner tous les caractères de la chaîne depuis le début (les deux-points sont avant le nombre, cela signifie «du début à») jusqu'au 4ème caractère (il inclut le 4ème personnage). La raison pour laquelle 3 est égal jusqu'à la 4ème position est parce que Python commence à 0. Ensuite, quand je mets le mot [-1:] , cela signifie le 2ème dernier caractère à la fin (les deux-points sont derrière le nombre). Mettre -1 fera compter Python à partir du dernier caractère, plutôt que du premier. Encore une fois, Python commencera à 0. Donc, le mot [-1:] signifie essentiellement "de l'avant-dernier caractère à la fin de la chaîne.

Donc, en coupant les caractères avant le caractère que je veux supprimer et les caractères après et en les prenant en sandwich, je peux supprimer le caractère indésirable. Pensez-y comme une saucisse.Au milieu, c'est sale, donc je veux m'en débarrasser. Je coupe simplement les deux extrémités que je veux puis les assemble sans la partie indésirable au milieu.

Si je veux supprimer plusieurs caractères consécutifs, je déplace simplement les nombres dans [] (partie de découpage). Ou si je veux supprimer plusieurs caractères de différentes positions, je peux simplement prendre en sandwich plusieurs tranches à la fois.

Exemples:

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

supprimé est égal à «cool».

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

supprimé est égal à «macs».

Dans ce cas, [3: 5] signifie caractère en position 3 à caractère en position 5 (à l'exclusion du caractère en position finale).

N'oubliez pas que Python commence à compter à 0 , vous devrez donc également le faire.


0

Essaye celui-là:

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

Cette méthode fonctionne bien en python 3.5.2


0

Vous pouvez utiliser le remplacement d'expression régulière du module re. L'utilisation de l'expression ^ vous permet de choisir exactement ce que vous voulez dans votre chaîne.

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

La sortie de ceci serait "Thisisabsurd". Seules les choses spécifiées après le symbole ^ apparaîtront.


0

La méthode chaîne replacene modifie pas la chaîne d'origine. Il laisse l'original seul et renvoie une copie modifiée.

Ce que vous voulez, c'est quelque chose comme: line = line.replace(char,'')

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

Cependant, créer une nouvelle chaîne à chaque fois qu'un caractère est supprimé est très inefficace. Je recommande plutôt ce qui suit:

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

Ci-dessous un .. sans utiliser le concept d'expression régulière ..

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

par exemple,

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

Pour supprimer tout le nombre de la chaîne



-1

Fractionnement récursif: s = chaîne; chars = caractères à supprimer

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)

exemple:

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

-1

# pour chaque fichier d'un répertoire, renommez le nom de fichier

   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

Même l'approche ci-dessous fonctionne

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

production: abcde


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.