2012-09-17 16 views
10

J'ai une chaîne qui peut avoir un motif de caractère répété, par ex.Regex pour supprimer le motif de caractère répété dans une chaîne

'xyzzyxxyzzyxxyzzyx' 

J'ai besoin d'écrire un regex qui remplacerait cette chaîne avec son plus petit motif répété:

'xyzzyxxyzzyxxyzzyx' becomes 'xyzzyx', 

'abcbaccbaabcbaccbaabcbaccba' becomes 'abcbaccba' 
+0

Le motif est-il connu ou recherchez-vous un motif répétitif dans une chaîne? – Joel

+1

Il cherche le plus petit motif répété je suppose. – arshajii

Répondre

5

Utilisez les éléments suivants:

> re.sub(r'(.+?)\1+', r'\1', 'xyzzyxxyzzyxxyzzyx') 
'xyzzyx' 
> re.sub(r'(.+?)\1+', r'\1', 'abcbaccbaabcbaccbaabcbaccba') 
'abcbaccba' 
> re.sub(r'(.+?)\1+', r'\1', 'iiiiiiiiiiiiiiiiii') 
'i' 

Il correspond essentiellement un motif qui se répète (.+?)\1+, et supprime tout sauf le motif répétitif, qui est capturé dans le premier groupe \1. Notez également que l'utilisation d'un qualificatif réticent ici, c'est-à-dire +?, fera beaucoup de backtrack dans la regex.

DEMO.

+0

Le problème avec ceci est qu'il échoue pour ce cas: >>> re.sub (r '(\ w +) \ 1+', r '\ 1', 'iiiiiiiiiiiiiiiiii') donne 'iiiiiiiii' au lieu d'un 'i' – mercador

+0

@mercador: Je vois, rendre le '' 'quantificateur réticent au lieu de glouton. J'ai mis à jour ma réponse. –

4

Puisque vous voulez que le motif répétitif le plus petit, quelque chose comme ce qui suit devrait travailler pour vous:

re.sub(r'^(.+?)\1+$', r'\1', input_string) 

Les ancres ^ et $ assurez-vous que vous ne recevez pas les matchs au milieu de la chaîne, et par en utilisant .+? au lieu de seulement .+ vous obtiendrez le plus court modèle (comparez les résultats en utilisant une chaîne comme 'aaaaaaaaaa').

+1

Et gardez juste à l'esprit que cela peut prendre beaucoup de temps à échouer si 'input_string' est quelque chose comme' "un" * 1000000 + "b" '. – hobbs

+1

Toutes les idées de regex sans retour arrière? Le '. +?' Provoquera un retour en arrière lourd. – Kash

+0

Si vous lisez un livre tel que 'Programming Perl', vous pouvez trouver une réalisation de regex avec des exemples 'lourds'. Je pense que ce n'est pas une tâche rapide pour les regex. – gaussblurinc

2

Essayer cette expression rationnelle et capturer le premier groupe:

^(.+?)\1+$ 
  • ^ ancrage pour le début de chaîne/ligne
  • . tout caractère à l'exception des sauts de ligne
  • + quantificateurs pour désigner atleast 1 occurence
  • ? rend le + paresseux au lieu de gourmand, d'où vous donnant le plus court motif
  • () groupe capture
  • \1+ backreference avec quantificateurs pour désigner ce modèle devrait répétition au moins une fois
  • $ point d'ancrage pour la fin de chaîne/ligne

test ici: Rubular


La solution ci-dessus fait beaucoup de bac ktracking affectant la performance. Si vous connaissez les caractères qui ne sont pas autorisés dans ces chaînes, vous pouvez utiliser un ensemble cadencé neutralisé qui élimine le retour arrière. Par exemple, si les espaces ne sont pas autorisés, alors

^([^\s]+)\1+$ 
Questions connexes