Je cherche à trouver une syntaxe d'expression régulière Perl concernant certaines exigences que j'ai dans un projet. Tout d'abord, je veux exclure des chaînes d'un fichier txt (dictionnaire).
Par exemple, si mon fichier contient ces chaînes:
\b(?:([a-z])(?!\1))+\b
j'utilise l'expression régulière
XXX
J'ai aussi une autre exigence que je n'ai pas pu résoudre. Je dois créer un Regex qui ne permet pas à une chaîne d'avoir plus de 3 fois une répétition de caractères (deux caractères).
Par exemple:
adminnisstrator21 -- match (have 2 times a repetition of chars) kkeeykloakk -- not match have over 3 times repetition stack22ooverflow -- match (have 2 times a repetition of chars)
pour ceci j'ai essayé
a1testtre -- match orangesh1 -- match apleship3 -- not match [contains word from file ]
mais cela ne fonctionne que pour le premier char-reppeat Une idée comment résoudre ces deux problèmes?
5 Réponses :
J'espère que quelqu'un d'autre viendra avec une meilleure solution, mais cela semble faire ce que vous voulez:
\b Match word boundary (?: Start capture group (?:([a-z0-9])(?!\1))* Match all characters until it encounters a double (?:([a-z0-9])\2)+ Match all repeated characters until a different one is reached ){0,2} Match capture group 0 or 2 times (?:([a-z0-9])(?!\3))+ Match all characters until it encounters a double \b Match end of word
J'ai changé le [az]
pour qu'il corresponde également les nombres, puisque les exemples que vous avez donnés semblent également inclure des nombres. Perl regex a également le raccourci \ w
, qui équivaut à [A-Za-z0-9_]
, ce qui peut être pratique si vous voulez faire correspondre n'importe quel caractère dans un mot.
Merci pour la réponse ... je vais l'essayer.En fait, ma plus grande préoccupation concerne la première exigence concernant les mots du dictionnaire
Pour ne pas correspondre à un mot d'un fichier, vous pouvez vérifier si une chaîne contient une sous-chaîne ou utilisez une anticipation négative et une alternative:
(?=^(?!(?:\w*(.)\1){3}).+$)(?=^(?:(.)(?!(?:.*?\2){4}))*$) ^
^
Affirmer le début de la chaîne (?!
lookahead négatif, affirmer que ce qui est à droite n'est pas
. * (?: tree | car | ship)
Correspond à 0 fois ou plus n'importe quel caractère sauf une nouvelle ligne et correspond à la voiture ou au bateau de l'arbre )
Fermer l'anticipation négative . *
Correspond à n'importe quel caractère sauf une nouvelle ligne $
Affirmer la fin de la chaîne Pour ne pas autoriser une chaîne pour avoir plus de 3 fois une répétition de caractères que vous pourriez utiliser:
(?=^(?!(?:\w*(.)\1){3}).+$)(?=^(?:(.)(?!(?:.*?\1){4}))*$)
\ b
Limite de mot (?!
Lookahead négatif, affirmer que ce qui est à droite n'est pas
(?:
AUCUN groupe de capture \ w * (\ w) \ 1
Correspond à 0 fois ou plus un caractère de mot suivi de la capture d'un caractère de mot dans un groupe suivi d'une référence arrière en utilisant \ 1
pour ce groupe ) {3}
Fermer le groupe sans capture et répéter 3 fois )
fermer l'anticipation négative \ w +
Correspond à 1+ caractères de mot \ b
limite de mot Mettre à jour
Selon cette réponse publiée (que vous pouvez ajouter à la question à la place) vous avez 2 modèles que vous voulez combiner mais cela ne fonctionne pas:
\b(?!(?:\w*(\w)\1){3})\w+\b
Dans ces 2 modèles, vous utilisez 2 groupes de capture, donc le deuxième modèle doit pointer vers le second groupe de capture \ 2
.
^(?!.*(?:tree|car|ship)).*$
Démo de modèle p >
Une façon d'exclure des chaînes contenant des mots d'une liste donnée est de former un modèle avec une alternance de mots et de l'utiliser dans une expression régulière, et d'exclure les chaînes pour lesquelles il correspond.
solely|sole|so
J'utilise Path :: Tiny pour lire le fichier dans une chaîne ("slurp" ), qui est ensuite divisé par des espaces en mots à utiliser pour l'exclusion. Le quotemeta échappe les caractères autres que des "mots", s'il y en a dans vos mots, ce qui sont ensuite joints par |
pour former une chaîne avec un motif regex. (Avec des modèles complexes, utilisez qr .)
Cela peut être possible d'ajuster et d'améliorer, en fonction de vos cas d'utilisation, pour l'un en ce qui concerne l'ordre des motifs avec des parties communes en alternance. †
Le contrôle que les caractères en double successifs font ne pas se produire plus de trois fois
my $exclude = join '|', map { quotemeta } sort { length $a <=> length $b } @words; #==> so|sole|solely
Une longue chaîne de caractères répétés ( aaaa
) compte pour une seule instance, en raison du + quantificateur dans regex; si vous préférez compter toutes les paires, supprimez les
+
et quatre a
compteront pour deux paires. Le même caractère répété à divers endroits dans la chaîne compte à chaque fois, donc aaXaa
compte pour deux paires.
Cet extrait de code peut être simplement ajouté au programme ci-dessus, qui est appelé avec le nom du fichier avec les mots à utiliser pour l'exclusion. Ils impriment tous les deux ce qui est attendu des échantillons fournis.
† Prenons un exemple avec des mots d'exclusion: so
, sole et
uniquement
. Si vous avez seulement besoin de vérifier si l'une de ces correspondances, vous voudrez des plus courtes en premier dans l'alternance
foreach my $string (qw(adminnisstrator21 kkeeykloakk stack22ooverflow)) { my @chars_that_repeat = $string =~ /(.)\1+/g; if (@chars_that_repeat < 3) { say "OK: $string"; } }
pour une correspondance plus rapide ( donc code > correspond aux trois). Cela semble être le cas ici.
Mais, si vous vouliez identifier correctement le mot correspondant, vous devez d'abord avoir des mots plus longs,
use warnings; use strict; use feature qw(say); use Path::Tiny; my $file = shift // die "Usage: $0 file\n"; #/ my @words = split ' ', path($file)->slurp; my $exclude = join '|', map { quotemeta } @words; foreach my $string (qw(a1testtre orangesh1 apleship3)) { if ($string !~ /$exclude/) { say "OK: $string"; } }
afin qu'une chaîne uniquement
soit correctement mise en correspondance par son mot avant qu'elle ne puisse être "volée" par donc
. Ensuite, dans ce cas, vous voudriez que ce soit l'inverse,
sort {length $ b length $ a}
En plus de la réponse de zdim à la première partie de votre question, soyez prudent lors de l'assemblage de l'expression régulière de @words
. L'ordre des mots joints avec «|» peut affecter l'appariement.
L'ordre des mots joints avec «|» peut affecter l'appariement. Par exemple, vous voudriez TRUSTEES avant TRUSTEE avant TRUST afin que le résultat de la jointure contienne les mots suivants: «TRUSTEES | TRUSTEE | TRUST». J'ai un petit sous-programme pour commander une liste de motifs / mots, que je posterai comme réponse.
@BruceVanAllen C'est la déclaration " Cela peut être possible à modifier et à améliorer, selon vos cas d'utilisation. " dans la réponse. Et en fait, étant donné ce qu'ils demandent précisément (ne trouver qu'une correspondance pour l'un des mots), vous voudrez en fait le commander dans l'autre sens, plus court d'abord - puis tous les mots qui partagent cette racine sont trouvés plus vite. Mais nous ne connaissons pas vraiment le cas d'utilisation réel, je n'ai donc laissé qu'une mention.
Mon problème est que j'ai 2 regex qui fonctionnent:
Ne pas autoriser plus de 3 paires de caractères:
(?=^(?!(?:\w*(.)\1){3}).+$)(?=^(?:(.)(?!(?:.*?\1){4}))*$)
Ne pas autoriser plus de 4 fois un caractère à se répéter:
(?=^(?:(.)(?!(?:.*?\1){4}))*$)
Maintenant, je veux les combiner en une seule ligne comme:
(?=^(?!(?:\w*(.)\1){3}).+$)
mais cela ne fonctionne que le regex qui est le premier et pas les deux
Vous devez pointer vers le deuxième groupe de capture à la place pour le deuxième modèle. J'ai ajouté une mise à jour à ma réponse.
Comme mentionné dans le commentaire de la réponse de @ zdim, allez un peu plus loin en vous assurant que l'ordre dans lequel vos mots sont assemblés dans le modèle de correspondance ne vous tracasse pas. Si les mots du fichier ne sont pas très soigneusement ordonnés pour commencer, j'utilise un sous-programme comme celui-ci lors de la construction de la chaîne de correspondance:
... my @words = split ' ', path($file)->slurp; @words = tight_match_order(@words); # add this line my $exclude = join '|', map { quotemeta } @words; ...
Donc, en suivant la réponse de @ zdim:
# Returns a list of alternative match patterns in tight matching order. # E.g., TRUSTEES before TRUSTEE before TRUST # TRUSTEES|TRUSTEE|TRUST sub tight_match_order { return @_ unless @_ > 1; my (@alts, @ordered_alts, %alts_seen); @alts = map { $alts_seen{$_}++ ? () : $_ } @_; TEST: { my $alt = shift @alts; if (grep m#$alt#, @alts) { push @alts => $alt; } else { push @ordered_alts => $alt; } redo TEST if @alts; } @ordered_alts }
HTH
Voir Que dois-je faire lorsque quelqu'un répond à ma question? (Vous pouvez voter sur les réponses en cliquant sur les flèches à côté d'eux, et acceptez-en une, en cliquant sur la coche)