2010-10-07 7 views
12

Je souhaite remplacer les instances répétées du caractère "*" dans une chaîne avec une seule instance de "*". Par exemple, si la chaîne est "***abc**de*fg******h", je souhaite la convertir en "*abc*de*fg*h".Comment remplacer des instances répétées d'un caractère avec une seule instance de ce caractère en python

Je suis assez nouveau pour python (et la programmation en général) et ont essayé d'utiliser des expressions régulières et String.replace() comme:

import re  
pattern = "***abc**de*fg******h" 
pattern.replace("*"\*, "*") 

\* est censé remplacer toutes les instances du « * " personnage. Mais j'ai: SyntaxError: caractère inattendu après le caractère de continuation de ligne.

J'ai aussi essayé de le manipuler avec une boucle comme:

def convertString(pattern): 
for i in range(len(pattern)-1): 
    if(pattern[i] == pattern[i+1]): 
     pattern2 = pattern[i] 
return pattern2 

mais cela a l'erreur où il ne imprime « * » parce que pattern2 = motif [i] redéfinit constamment ce que pattern2 est ...

Toute aide serait appréciée.

Répondre

4

Je suggère d'utiliser la fonction re module sous:

import re 

result = re.sub("\*+", "*", "***abc**de*fg******h") 

Je recommande vivement la lecture à travers l'article sur RE et de bonnes pratiques. Ils peuvent être difficiles si vous n'êtes pas familier avec eux. En pratique, l'utilisation de chaînes brutes est une bonne idée.

+0

Merci beaucoup, cela fonctionne bien, je vais lire l'article sur RE pour savoir ce qui se passe exactement avec la partie "\ +" du code. Je ne savais pas que vous pouviez utiliser plusieurs symboles en même temps. Je pensais que vous ne pouviez utiliser que "+" ou "*" par exemple. – NSchrading

+2

@NSchrading: Dans '" \\ * + "', j'échappe au caractère * car c'est un symbole re spécial. Donc, je fais correspondre un caractère littéral *, et le + signifie un ou plusieurs. – JoshD

0
re.sub('\*+', '*', pattern) 

Cela fera l'affaire.

1

Expressions bien régulières sage je ferais exactement comme JoshD a suggéré. Mais une amélioration ici.

utilisation -

regex = re.compile('\*+') 
result = re.sub(regex, "*", string) 

Ce serait essentiellement votre cache regex. L'utilisation ultérieure de ceci dans une boucle rendrait vos opérations regex rapides.

+1

Ceci est une optimisation prématurée. Python met en cache des regex compilés récemment utilisés de toute façon. – kindall

0

sans regexp vous pouvez utiliser le retrait de l'élément répétitif général avec vérification de « * »:

source = "***abc**dee*fg******h" 
target = ''.join(c for c,n in zip(source, source[1:]+' ') if c+n != '**') 
print target 
4

que diriez-vous d'une manière non regex

def squeeze(char,s): 
    while char*2 in s: 
     s=s.replace(char*2,char) 
    return s 
print squeeze("*" , "AB***abc**def**AA***k") 
14

La façon naïve de faire ce genre de chose avec re est

re.sub('\*+', '*', text) 

qui remplace pistes de 1 ou plusieurs astérisques avec un astérisque. Pour des courses d'exactement un astérisque, cela fonctionne très dur juste pour rester immobile. Beaucoup mieux est de remplacer pistes de deux ou plusieurs astérisques par un astérisque:

re.sub('\*\*+', '*', text) 

Cela peut être bien la peine de faire:

\python27\python -mtimeit -s"t='a*'*100;import re" "re.sub('\*+', '*', t)" 
10000 loops, best of 3: 73.2 usec per loop 

\python27\python -mtimeit -s"t='a*'*100;import re" "re.sub('\*\*+', '*', t)" 
100000 loops, best of 3: 8.9 usec per loop 

Notez que re.sub renverra une référence à la chaîne d'entrée si elle n'a pas trouvé de correspondance, ce qui économisera plus d'usure sur votre ordinateur, au lieu d'une toute nouvelle chaîne.

+0

+1 C'est un bon conseil. Bien que pour un Joe moyen qui pourrait ne pas le faire des milliers de fois par seconde, la différence importe peu. Le premier gagne sur la lisibilité. Donc, je suggère, n'utilisez pas aveuglément ce dernier. – Medorator

+1

@Medorator Je ne suis pas sûr de la lisibilité ... une fois que JAvg a compris '\ X', le saut vers' \ X \ X' semble minuscule pour moi. Peut-être que \ X {2} serait mieux? J'espère que JAvg ne ferait rien aveuglément. Si l'exigence de sortie a changé pour "trouver toutes les occurrences d'astérisques répétés", vous voudriez penser que l'expression regex est passée de 1+ à 2+. –

1

Vous avez écrit:

pattern.replace("*"\*, "*") 

que vous vouliez dire:

pattern.replace("\**", "*") 
#    ^^^^ 

Vous signifiait vraiment:

pattern_after_substitution= re.sub(r"\*+", "*", pattern) 

qui fait ce que vous vouliez.

0

Supposons dans cet exemple que votre personnage est un espace.

Vous pouvez aussi le faire de cette façon:

while True: 
    if " " in pattern: # if two spaces are in the variable pattern 
     pattern = pattern.replace(" ", " ") # replace two spaces with one 
    else: # otherwise 
     break # break from the infinite while loop 

Ce:

File Type      : Win32 EXE 
File Type Extension    : exe 
MIME Type      : application/octet-stream 
Machine Type     : Intel 386 or later, and compatibles 
Time Stamp      : 2017:04:24 09:55:04-04:00 

Devient:

File Type : Win32 EXE 
File Type Extension : exe 
MIME Type : application/octet-stream 
Machine Type : Intel 386 or later, and compatibles 
Time Stamp : 2017:04:24 09:55:04-04:00 

Je trouve cela est un peu plus facile que d'avoir à muck autour avec le re module, qui peut parfois être un peu gênant (je pense).

J'espère que cela a été utile.

0

Ceci fonctionnera pour n'importe quel nombre d'astérisques consécutifs, bien que vous ayez besoin de remplacer le tilde par une autre chaîne dont vous savez qu'elle sera unique dans toute la chaîne. Je crois que les approches regex seraient généralement plus coûteuses en termes de calcul que celles-ci.

Questions connexes