TL;DR
re.search("(.)(?!.*\1)", text).group()
ne correspond pas à la première non-répétition des caractères figurant dans le texte (il renvoie toujours un personnage au ou avant le premier non-caractère répété, ou avant la fin de la chaîne si il n'y a pas non caractères répétés. Ma compréhension est que la ré.recherche() doit renvoyer None s'il n'y avait pas de matchs).
Je suis seulement intéressé à comprendre pourquoi cette regex ne fonctionne pas comme prévu à l'aide de l'Python re
module, non pas dans un autre moyen de résoudre le problème
Historique Complet
La description du problème vient de la https://www.codeeval.com/open_challenges/12/. J'ai déjà résolu ce problème en utilisant un non-regex méthode, mais revisité à élargir ma compréhension de Python re
module.
Les expressions régulières me semblait (nommé vs sans nom dans les références arrières) sont:
(?P<letter>.)(?!.*(?P=letter))
et (.)(?!.*\1)
(mêmes résultats en python2 et python3)
L'ensemble de mon programme ressemble à ceci
import re
import sys
with open(sys.argv[1], 'r') as test_cases:
for test in test_cases:
print(re.search("(?P<letter>.)(?!.*(?P=letter))",
test.strip()
).group()
)
et certaines paires d'entrées/sorties sont:
rain | r
teetthing | e
cardiff | c
kangaroo | k
god | g
newtown | e
taxation | x
refurbished | f
substantially | u
Selon ce que j'ai lu à https://docs.python.org/2/library/re.html:
-
(.)
crée un groupe nommé qui correspond à tout caractère et permet plus tard de références arrières comme\1
. -
(?!...)
est une anticipation négatif, ce qui limite correspond aux cas où l'...
ne correspond pas. -
.*\1
signifie n'importe quel nombre (y compris zéro) de caractères suivie par tout ce qui a été compensée par(.)
précédemment -
re.search(pattern, string)
retourne uniquement le premier endroit où l'regex modèle produit un match (et le retour Aucune si aucune correspondance n'a pu être trouvé) -
.group()
est équivalent à.group(0)
qui renvoie l'intégralité du match
Je pense que ces pièces ensemble devrait résoudre le problème posé, et il ne fonctionne pas comme je pense qu'il doit pour la plupart des entrées, mais a échoué sur teething
. Jeter des problèmes semblables à lui révèle qu'il semble ignorer les caractères répétés si elles sont consécutives:
tooth | o # fails on consecutive repeated characters
aardvark | d # but does ok if it sees them later
aah | a # verified last one didn't work just because it was at start
heh | e # but it works for this one
hehe | h # What? It thinks h matches (lookahead maybe doesn't find "heh"?)
heho | e # but it definitely finds "heh" and stops "h" from matching here
hahah | a # so now it won't match h but will match a
hahxyz | a # but it realizes there are 2 h characters here...
hahxyza | h # ... Ok time for StackOverflow
Je sais lookbehind et négatifs lookbehind sont limités à 3 caractères max chaînes de longueur fixe, et ne peut pas contenir de références arrières, même si ils évaluent à une chaîne de longueur fixe, mais je n'ai pas voir la documentation de préciser les restrictions sur l'anticipation négatif.