2010-07-22 2 views
8

Étant donné une chaîne d'identificateurs séparés par :, est-il possible de construire une expression régulière pour extraire les identificateurs uniques dans une autre chaîne, également séparés par :?Quelle expression régulière peut supprimer des éléments en double d'une chaîne?

Comment est-il possible de réaliser cela en utilisant une expression régulière? J'ai essayé s/(:[^:])(.*)\1/$1$2/g sans aucune chance, parce que le (.*) est gourmand et saute au dernier match de $1.

Exemple: a:b:c:d:c:c:x:c:c:e:e:f devrait donner a:b:c:d:x:e:f

Note: Je suis codage en Perl, mais je serais très reconnaissant à l'aide d'une expression rationnelle pour cela.

+1

pourriez-vous s'il vous plaît afficher un exemple de ce que vous cherchez, je ne comprends pas très bien. – Anders

Répondre

8

Dans .NET qui prend en charge la répétition infinie à l'intérieur lookbehind, vous pouvez rechercher

(?<=\b\1:.*)\b(\w+):? 

et remplacer toutes les correspondances avec la chaîne vide.

Perl (au moins Perl 5) ne supporte que assertions arrières de longueur fixe, de sorte que vous pouvez essayer les éléments suivants (en utilisant préanalyse, avec un résultat légèrement différent):

\b(\w+):(?=.*\b\1:?) 

Si vous remplacez que par la chaîne vide , tous les précédents répétitions d'une entrée en double seront supprimés; le dernier un restera. Ainsi, au lieu de

a:b:c:d:x:e:f 

vous obtiendrez

a:b:d:x:c:e:f 

Si tel est OK, vous pouvez utiliser

$subject =~ s/\b(\w+):(?=.*\b\1:?)//g; 

Explication:

Première regex:

(?<=\b\1:.*): Vérifiez si vous pouvez faire correspondre le contenu de la référence arrière no. 1, suivi d'un deux-points, quelque part avant dans la chaîne.

\b(\w+):?: Faire correspondre un identificateur (à partir d'une limite de mot à l'autre :), éventuellement suivie de deux points.

Deuxième regex:

\b(\w+):: Faites correspondre un identifiant et deux points.

(?=.*\b\1:?): Ensuite, vérifiez si vous pouvez faire correspondre le même identificateur, éventuellement suivi d'un deux-points, quelque part en avant dans la chaîne.

+0

L'ordre de sortie est sans importance pour moi, c'est pourquoi je ne l'ai pas mentionné dans la question (peut-être que j'aurais dû mentionner que ce n'était pas pertinent :). Merci, ça a marché comme un charme! – Tom

+0

Veuillez mettre à jour votre réponse, la solution que vous avez fournie ne fonctionnait que si les mots contenaient un caractère. J'ai oublié de mentionner cela aussi. Une meilleure réponse serait 's/\ b (\ w +): (? =. * \ 1:?) // g' – Tom

+0

@Tom: Excellent point. J'ai mis à jour ma réponse. Le mot affirmation de limite est également nécessaire devant la référence arrière. –

0

Si les identifiants sont triés, vous pouvez le faire en utilisant lookahead/lookbehind. Si ce n'est pas le cas, cela dépasse la puissance de calcul d'une regex. Maintenant, juste parce que c'est impossible avec une regex formelle ne signifie pas que c'est impossible si vous utilisez une fonction regex spécifique à perl, mais si vous voulez garder vos expressions régulières portables, vous devez décrire cette chaîne dans un langage qui supporte les variables.

+0

Le tri n'est pas pertinent, voir ma solution. –

+0

Qu'entendez-vous par caractéristiques spécifiques à Perl? Capturer des groupes, des références arrières, des limites de mots et des retards de vue sont très largement supportés. Parmi les fonctionnalités utilisées dans cette discussion, la seule que j'appellerais non-portable est lookbehinds, surtout lookbehinds sans limite. –

+0

@Tim: Je dirais que c'est pertinent dans le sens où, si les identifiants étaient triés, l'élimination des doublons serait triviale: 's/(\ w +) (: \ 1) + (? =: | $)/$ 1/g' –

1
$str = q!a:b:c:d:c:c:x:c:c:e:e:f!; 

1 while($str =~ s/(:[^:]+)(.*?)\1/$1$2/g); 

say $str 

sortie:

a:b:c:d:x:e:f 
+0

+1 pour une boucle while vide, bien que je pense qu'une solution plus complète pourrait être: 'while {$ str = ~ s/(: [^:] + | [^:] +:) (. *) \ 1 (. *)/$ 1 $ 2 $ 3/g} 'pour vérifier la première lettre. – NorthGuard

0

Voici une version awk, pas besoin de regex. Diviser les champs sur ":", parcourir les champs divisés, stocker les éléments dans un tableau. vérifier l'existence et si existe, sauter. Sinon, imprimez-les. vous pouvez traduire ceci facilement en code Perl.

Questions connexes