11
votes

Comment supprimer les caractères en double et garder l'unique que dans Perl?

Comment puis-je supprimer les caractères en double et garder l'unique uniquement. Par exemple, mon entrée est la suivante: xxx

la sortie attendue est la suivante: xxx

je suis tombé sur perl -pe's / 1 $ // $ g tandis que /(.). [^\/'< / code> qui est merveilleux mais il retire même l'occurrence unique du caractère en sortie.


0 commentaires

11 Réponses :


15
votes

Ceci peut être fait à l'aide de positif lookahead : xxx

La regex utilisée est: (.) (? =. *? \ 1)

  • . : correspondre à n'importe quel caractère.
  • premier () : rappelez-vous le correspondant Simple Char.
  • (? = ...) : + ve lookahead
  • . *? : Pour faire correspondre n'importe quoi entre
  • \ 1 : le match mémorisé.
  • (.) (? =. *? \ 1) : correspondez et rappelez-vous tout char seulement si il apparaît à nouveau plus tard dans la chaîne.
  • s /// : Perl mode de faire le substitution.
  • g : faire la substitution globalement ... ça ne s'arrête pas après première substitution.
  • s / (.) (? =. *? \ 1) // g : Ce sera Supprimer un char de la chaîne d'entrée Seulement si ce caractère apparaît à nouveau plus tard dans la chaîne.

    Ce sera pas maintenir l'ordre du char en entrée car pour chaque caractère unique dans la chaîne d'entrée, nous conservent son < em> dernier occurrence et non le premier .

    pour garder l'ordre relatif intact que nous pouvons faire ce que Kennytm raconte dans l'un des commentaires:

    • Inverser la ligne d'entrée
    • faire la substitution comme avant
    • Inversez le résultat avant d'imprimer

      la ligne Perl Une ligne pour ceci est la suivante: xxx

      puisque nous faisons imprimer manuellement après inversion, nous ne le faisons pas Utilisez le drapeau -p mais utilisez le drapeau -n .

      Je ne sais pas si c'est le meilleur one-liner pour le faire. Je souhaite la bienvenue aux autres pour éditer cette réponse s'ils ont une meilleure alternative.


4 commentaires

@Gavin: cela peut être corrigé en inversant la chaîne initialement et inverser la chaîne après le remplacement.


Eh bien c'est incroyable !!!! Mais pouvez-vous m'expliquer des détails du bit comme ce que ====> s / (.) Et (? =. *? \ 1) // fait? Il est également possible d'avoir dans le même ordre que j'ai mis dans ma première requête, pour ex. Actuellement, je reçois Efahu au lieu d'Efuah qui est plus utile. Thnax a tonne :)


@KennyTM: Merci :) @manu: J'ai mis à jour mes ans avec une courte explication de ce qui se passe.


Cela fonctionne exactement. Merci encore pour la réponse aimable et expliquer clairement toutes les choses. Merci tous :)



0
votes

Pour un fichier contenant les données que vous liste nommées foo.txt xxx


1 commentaires

Son message original n'a pas spécifié Perl comme une exigence (bien qu'il l'a marqué Perl), il n'a fait que souligné qu'il a trouvé un Perl One-Liner comme un moyen possible de le faire. Il n'a pas non plus dit que la commande importait, seulement unicité. En outre, l'utilisation d'un one-liner indique que la méthode n'a pas d'importance.



1
votes

Cravate :: ixhash est un bon module pour stocker l'ordre de hachage (mais peut être lent, vous devrez faire référence si la vitesse est importante). Exemple avec des tests:

use Test::More 0.88;

use Tie::IxHash;
sub dedupe {
  my $str=shift;
  my $hash=Tie::IxHash->new(map { $_ => 1} split //,$str);
  return join('',$hash->Keys);
}

{
my $str='EFUAHUU';
is(dedupe($str),'EFUAH');
}

{
my $str='EFUAHHUU';
is(dedupe($str),'EFUAH');
}

{
my $str='UJUJHHACDEFUCU';
is(dedupe($str),'UJHACDEF');
}

done_testing();


0 commentaires

4
votes
perl -ne'my%s;print grep!$s{$_}++,split//'

4 commentaires

Cela fonctionne également et plus court qu'auparavant. Je suis submergé par la réponse :) J'aimerais connaître son travail si possible.


Cela fonctionne de la même manière que la solution de Gianthare, mais beaucoup plus de perl idiomatique et plus rapidement.


Un gentil, je suis d'accord. Presque une doublure sauf pour le mon% s . Bien que je ne vois pas d'où vient le speedUp. Qu'il s'agisse d'une hache fraîche au lieu de réinitialiser? Ou Grep est-il plus efficace que la boucle explicite?


@giantHare: Il y a la différence appelant impression pour chaque caractère et appelant impression avec le paramètre Array. Votre code sera plus lent pour les lignes avec une plus grande quantité de caractères uniques. % vu = (); devrait être presque aussi rapide que le mien mon% s; .



0
votes

de la coquille, cela fonctionne:

cat test.txt | while read line ; do echo $line | sed -e 's/./&\n/g' | sort | uniq | sed -e :a -e '$!N; s/\n//; ta' ; done


0 commentaires

1
votes

Ceci ressemble à une application classique de la vue positive, mais malheureusement, Perl ne supporte pas cela. En fait, le faire (correspondant au texte précédent d'un caractère dans une chaîne avec une regex complète dont la longueur est indéterminable) ne peut être effectuée que sur des classes de regex .net, je pense.

Cependant, Lookahead positif soutient des expulsions complètes, donc Tout ce que vous avez à faire est d'inverser la chaîne, appliquez un lookahead positif (comme unicornaddict dit): xxx

et inversez-le, car sans l'inverse qui ne conserve que le double caractère à la dernière place dans une ligne.

Massive Modification

Je passe la dernière demi-heure à ce sujet, et cela ressemble à ceci Fonctionne, sans l'inversion . xxx

Je ne sais pas s'il faut être fier ou horrible. Je fais essentiellement la looakahead positive, puis remplacez sur la chaîne avec \ g spécifiée - ce qui rend le moteur de regex démarrer sa correspondance de la dernière place correspondée (représentée en interne de la variable POS ()).

Avec une entrée de test comme ceci:

AABBBCBBCCBABB

efauuuuh

abcbbbbd

DEEEFGGH

AABBCC

La sortie est comme ceci:

abc

efauh

abcd

DEFGH

abc

i pense Ça fonctionne ...

Explication - D'accord, au cas où mon explication n'était pas assez claire - Le lookahead ira et s'arrêtera au dernier match d'une variable en double [dans le code, vous pouvez faire un POS d'impression (); À l'intérieur de la boucle pour vérifier] et le S / \ g // G supprimera-le [Vous n'avez pas besoin de / g vraiment]. Donc, dans la boucle, la substitution continuera à éliminer jusqu'à ce que tous ces doublons soient zappés. Bien sûr, cela pourrait être un peu trop processeur intensif pour vos goûts ... mais la plupart des solutions à base de regex que vous verrez. La méthode d'inversion / lookahead sera probablement plus efficace que cela, cependant.


2 commentaires

Plus précisément, c'est la longueur de la variable lookbehinds perl ne prend pas en charge. Outre .NET, ils sont pris en charge par JGSoft (EditPad Pro, PowerGrep) et dans une forme plus limitée de Java.


Édité et ajouté une nouvelle solution. Je ne sais pas si c'est complet ou non ... trop de caféine. :-P




1
votes

Si l'ensemble de caractères pouvant être rencontrés est restreint, par exemple Seules les lettres, alors la solution la plus facile sera avec TR < Perl -P -E 'TR / A-ZA-Z / A-ZA-Z / S'

Il remplacera toutes les lettres par elles-mêmes, laissant d'autres caractères non affectés et / s modifier va transmettre des occurrences répétées du même caractère (après le remplacement), éliminant ainsi des doublons

moi mauvais - il ne supprime que des apparences adjacentes. Ne tenez pas compte


0 commentaires

4
votes

Voici une solution, que je pense devoir travailler plus vite que la lookahead, mais elle n'est pas à la fois basée sur la réégalité et utilise hashtable.

perl -n -e '%seen=();' -e 'for (split //) {print unless $seen{$_}++;}' 


0 commentaires

0
votes
EFUAH
UEH
UJHACDEF

0 commentaires

5
votes

Si Perl n'est pas un must, vous pouvez également utiliser AWK. Voici une référence amusante sur le Perl One Liners posté contre Awk. Awk est 10+ secondes plus rapidement pour un fichier avec 3Million ++ lignes xxx


1 commentaires

Je suis étonné de la rapidité avec laquelle la solution Regexp est rapide