Remplacement insensible à la casse

173

Quel est le moyen le plus simple de remplacer une chaîne insensible à la casse en Python?

Adam Ernst
la source

Réponses:

217

Le stringtype ne prend pas en charge cela. Il vaut probablement mieux utiliser la sous-méthode d' expression régulière avec l' option re.IGNORECASE .

>>> import re
>>> insensitive_hippo = re.compile(re.escape('hippo'), re.IGNORECASE)
>>> insensitive_hippo.sub('giraffe', 'I want a hIPpo for my birthday')
'I want a giraffe for my birthday'
Blair Conrad
la source
11
Si vous ne faites qu'un seul remplacement ou que vous souhaitez enregistrer des lignes de code, il est plus efficace d'utiliser une seule substitution avec re.sub et le drapeau (? I): re.sub ('(? I)' + re .escape ('hippo'), 'girafe', 'I want a hIPpo for my birthday')
D Coetzee
3
Pourquoi re.escape pour une chaîne de lettres uniquement? Merci.
Elena
8
@Elena, ce n'est pas nécessaire pour 'hippo', mais serait utile si la valeur à remplacer était passée dans une fonction, donc c'est vraiment plus un bon exemple qu'autre chose.
Blair Conrad
2
En plus d'avoir à re.escapevotre aiguille, il y a un autre piège ici que cette réponse ne parvient pas à éviter, noté dans stackoverflow.com/a/15831118/1709587 : puisque re.subtraite les séquences d'échappement, comme indiqué dans docs.python.org/library/re.html#re .sub , vous devez soit échapper toutes les barres obliques inverses dans votre chaîne de remplacement, soit utiliser un lambda.
Mark Amery
84
import re
pattern = re.compile("hello", re.IGNORECASE)
pattern.sub("bye", "hello HeLLo HELLO")
# 'bye bye bye'
Inconnue
la source
17
Ou one-liner: re.sub('hello', 'bye', 'hello HeLLo HELLO', flags=re.IGNORECASE)
Louis Yang
Notez que re.subne prend en charge cet indicateur que depuis Python 2.7.
fuenfundachtzig
47

En une seule ligne:

import re
re.sub("(?i)hello","bye", "hello HeLLo HELLO") #'bye bye bye'
re.sub("(?i)he\.llo","bye", "he.llo He.LLo HE.LLO") #'bye bye bye'

Ou, utilisez l'argument optionnel "flags":

import re
re.sub("hello", "bye", "hello HeLLo HELLO", flags=re.I) #'bye bye bye'
re.sub("he\.llo", "bye", "he.llo He.LLo HE.LLO", flags=re.I) #'bye bye bye'
viebel
la source
14

Poursuivant sur la réponse de bFloch, cette fonction ne changera pas une, mais toutes les occurrences d'ancien avec nouveau - sans tenir compte de la casse.

def ireplace(old, new, text):
    idx = 0
    while idx < len(text):
        index_l = text.lower().find(old.lower(), idx)
        if index_l == -1:
            return text
        text = text[:index_l] + new + text[index_l + len(old):]
        idx = index_l + len(new) 
    return text
rsmoorthy
la source
Très bien fait. Bien mieux que regex; il gère toutes sortes de caractères, alors que l'expression régulière est très pointilleuse sur tout ce qui n'est pas alphanumérique. Réponse préférée IMHO.
fyngyrz
Tout ce que vous avez à faire est d'échapper à l'expression régulière: la réponse acceptée est beaucoup plus courte et plus facile à lire que cela.
Mad Physicist
Escape ne fonctionne que pour la correspondance, les barres obliques inverses dans la destination peuvent encore gâcher les choses.
ideasman42
4

Comme Blair Conrad dit que string.replace ne prend pas en charge cela.

Utilisez l'expression régulière re.sub, mais n'oubliez pas d'échapper d'abord à la chaîne de remplacement. Notez qu'il n'y a pas d'option flags dans la version 2.6 pour re.sub, vous devrez donc utiliser le modificateur intégré '(?i)'(ou un objet RE, voir la réponse de Blair Conrad). De plus, un autre piège est que sub traitera les échappements de barre oblique inverse dans le texte de remplacement, si une chaîne est donnée. Pour éviter cela, on peut à la place passer dans un lambda.

Voici une fonction:

import re
def ireplace(old, repl, text):
    return re.sub('(?i)'+re.escape(old), lambda m: repl, text)

>>> ireplace('hippo?', 'giraffe!?', 'You want a hiPPO?')
'You want a giraffe!?'
>>> ireplace(r'[binfolder]', r'C:\Temp\bin', r'[BinFolder]\test.exe')
'C:\\Temp\\bin\\test.exe'
johv
la source
4

Cette fonction utilise à la fois les fonctions str.replace()et re.findall(). Il remplacera toutes les occurrences de patternin stringpar repld'une manière insensible à la casse.

def replace_all(pattern, repl, string) -> str:
   occurences = re.findall(pattern, string, re.IGNORECASE)
   for occurence in occurences:
       string = string.replace(occurence, repl)
       return string
Nico Bako
la source
3

Cela ne nécessite pas RegularExp

def ireplace(old, new, text):
    """ 
    Replace case insensitive
    Raises ValueError if string not found
    """
    index_l = text.lower().index(old.lower())
    return text[:index_l] + new + text[index_l + len(old):] 
bFloch
la source
3
Bon, cependant cela ne change pas toutes les occurrences de l'ancien avec le nouveau, mais seulement la première occurrence.
rsmoorthy
5
C'est moins lisible que la version regex. Inutile de réinventer la roue ici.
Johannes Bittner
Il serait intéressant de faire une comparaison des performances entre celle-ci et les versions votées, cela pourrait être plus rapide, ce qui compte pour certaines applications. Ou cela peut être plus lent car cela fonctionne plus en Python interprété.
D Coetzee
2

Une observation intéressante sur les détails de la syntaxe et les options:

Python 3.7.2 (tags / v3.7.2: 9a3ffc0492, 23 décembre 2018, 23:09:28) [MSC v.1916 64 bits (AMD64)] sur win32

import re
old = "TREEROOT treeroot TREerOot"
re.sub(r'(?i)treeroot', 'grassroot', old)

`` à la base à la base à la base ''

re.sub(r'treeroot', 'grassroot', old)

'TREEROOT grassroot TREerOot'

re.sub(r'treeroot', 'grassroot', old, flags=re.I)

`` à la base à la base à la base ''

re.sub(r'treeroot', 'grassroot', old, re.I)

'TREEROOT grassroot TREerOot'

Ainsi, le préfixe (? I) dans l'expression de correspondance ou l'ajout de "flags = re.I" comme quatrième argument entraînera une correspondance insensible à la casse. MAIS, utiliser simplement "re.I" comme quatrième argument ne donne pas de correspondance insensible à la casse.

En comparaison,

re.findall(r'treeroot', old, re.I)

['TREEROOT', 'treeroot', 'TREerOot']

re.findall(r'treeroot', old)

['racine d'arbre']

Murray
la source
Cela ne répond pas à la question. veuillez modifier votre réponse pour vous assurer qu'elle s'améliore par rapport aux autres réponses déjà présentes dans cette question.
hongsy
1

Je n'avais pas été converti en séquences d'échappement ( faites défiler un peu vers le bas), alors j'ai noté que re.sub convertit les caractères échappés antislashés en séquences d'échappement.

Pour éviter cela, j'ai écrit ce qui suit:

Remplacez insensible à la casse.

import re
    def ireplace(findtxt, replacetxt, data):
        return replacetxt.join(  re.compile(findtxt, flags=re.I).split(data)  )

De plus, si vous voulez qu'il soit remplacé par les caractères d'échappement, comme les autres réponses ici qui obtiennent les caractères bashslash de signification spéciale convertis en séquences d'échappement, décodez simplement votre recherche et, ou remplacez la chaîne. Dans Python 3, vous devrez peut-être faire quelque chose comme .decode ("unicode_escape") # python3

findtxt = findtxt.decode('string_escape') # python2
replacetxt = replacetxt.decode('string_escape') # python2
data = ireplace(findtxt, replacetxt, data)

Testé en Python 2.7.8

J'espère que cela pourra aider.

Stan S.
la source
0

Je n'ai jamais posté de réponse auparavant et ce fil est vraiment vieux mais j'ai trouvé une autre solution et j'ai pensé que je pourrais obtenir votre réponse, je ne suis pas expérimenté en programmation Python, donc s'il y a des inconvénients apparents, veuillez les signaler depuis son bon apprentissage: )

i='I want a hIPpo for my birthday'
key='hippo'
swp='giraffe'

o=(i.lower().split(key))
c=0
p=0
for w in o:
    o[c]=i[p:p+len(w)]
    p=p+len(key+w)
    c+=1
print(swp.join(o))
Anddan
la source
2
Pour apprendre: en général, lorsque vous effectuez une recherche et un remplacement sur une chaîne, il vaut mieux ne pas avoir à la transformer d'abord en tableau. C'est pourquoi la première réponse est probablement la meilleure. Lorsqu'il utilise un module externe, il traite la chaîne comme une chaîne entière. C'est aussi un peu plus clair ce qui se passe dans le processus.
isaaclw
Pour apprendre: c'est très difficile pour un développeur sans contexte de lire ce code et de déchiffrer ce qu'il fait :)
Todd