Clause de non-responsabilité: j'ai trouvé pas mal de questions similaires, mais pas la plus précise. Une fois que quelqu'un a répondu, je le supprimerai.
Je dois trouver tous les mots masqués tels que:
AAAAA aime les pommes, mais BBBBB aime les bananes. Leurs numéros de téléphone sont ffffr et ggggh.
Le motif est au moins trois fois un caractère répété.
Quand j'utilise:
m = ['AAAAA', 'BBBBB', 'ffffr', 'ggggh']
J'obtiens simplement tous les mots (qui contiennent plus de 3 caractères).
Idéalement, je ne devrais obtenir que:
import re p = re.compile(r'[a-z]{3,}, re.IGNORECASE) m = p.findall('AAAAA likes apples, but BBBBB likes bananas. Their phone numbers are ffffr and ggggh.')
Comment dois-je changer les règles de rage pour n'en capturer que celles-ci?
Merci !
3 Réponses :
Votre regex actuelle vérifie simplement trois [a-z]
ou plus, mais pas de répétition. Pour vérifier si une lettre est répétée, vous devez capturer et backreference plus tard. Utilisation de votre re.IGNORECASE
\b\w*?([a-z])\1\1\w*\b
\ b
correspond à une limite de mots li>
\ w
correspond à un caractère de mot li>
([a-z])
capture un caractère alphabétique en \1
\ 1
est une référence arrière à ce qui est capturé par le premier groupe Ce serait correspond à au moins 3 [az]
répétés entourés de n'importe quelle quantité de \ w
caractères de mot.
Merci! Je pensais qu'il devrait y avoir une solution beaucoup plus simple, mais je dois absolument mieux me renseigner sur les regex.
@ArnoldKlein Bienvenue! J'ai eu le temps d'ajouter un peu plus d'explications. Heureux que cela ait aidé =)
Ici, si nous souhaitons capturer un mot, nous utiliserions une limite de mot avec un back-référencement avec une expression similaire à:
# coding=utf8 # the above tag defines encoding for this document and is for Python 2.x compatibility import re regex = r"\b([a-z])\1\1\1.+?\b" test_str = "AAAAA likes apples, but BBBBB likes bananas. Their phone numbers are ffffr and ggggh." matches = re.finditer(regex, test_str, re.MULTILINE | re.IGNORECASE) for matchNum, match in enumerate(matches, start=1): print ("Match {matchNum} was found at {start}-{end}: {match}".format(matchNum = matchNum, start = match.start(), end = match.end(), match = match.group())) for groupNum in range(0, len(match.groups())): groupNum = groupNum + 1 print ("Group {groupNum} found at {start}-{end}: {group}".format(groupNum = groupNum, start = match.start(groupNum), end = match.end(groupNum), group = match.group(groupNum))) # Note: for Python 2.7 compatibility, use ur"" to prefix the regex and u"" to prefix the test string and substitution.
\b([a-z])\1\1\1.+?\b
Vous pouvez utiliser une expression régulière, mais je suggère d'utiliser une autre méthode, à savoir:
import string clear_found = [i.rstrip(string.punctuation) for i in found] print(clear_found) # ['AAAAA', 'BBBBB', 'ffffr', 'ggggh']
Notez que maintenant found
n'est pas exactement le même que votre sortie souhaitée, car de .
dans le dernier élément, mais nous pourrions facilement supprimer toute ponctuation de fin de la manière suivante:
txt = 'AAAAA likes apples, but BBBBB likes bananas. Their phone numbers are ffffr and ggggh.' words = txt.split(' ') found = [i for i in words if len(set(i[:3].lower()))==1] print(found) # ['AAAAA', 'BBBBB', 'ffffr', 'ggggh.']
Explication de ma méthode: j'obtiens 3 premiers caractères de mot, mettez-les tous en minuscules, puis utilisez set
pour vérifier s'il n'y a qu'une seule lettre (caractère). Vous pouvez également utiliser la méthode .upper
de str
. N'hésitez pas à utiliser une solution basée sur les regex si vous la jugez mieux adaptée à votre cas d'utilisation, mais gardez à l'esprit qu'il existe une possibilité de solution non-regex pour certains problèmes.
Intéressant! Je vais essayer.