Première publication, je me suis caché pendant un petit moment, vraiment excité par la communauté utile ici.

Donc, travailler avec "Automatiser les trucs ennuyeux" par Al Sweigart

Faire un exercice qui nécessite que je construise une expression régulière qui trouve des nombres au format numérique standard. Trois chiffres, virgule, trois chiffres, virgule, etc ...

J'espère donc que cela correspondra à 1 234 et 23 322 et 1 234 567 et 12 mais pas à 1,23,1 ou ,, 1111, ou quoi que ce soit d'autre idiot.

J'ai ce qui suit.

import re

testStr = '1,234,343'
matches = []
numComma = re.compile(r'^(\d{1,3})*(,\d{3})*$')

for group in numComma.findall(str(testStr)):
    Num = group
    print(str(Num) + '-')           #Printing here to test each loop
    matches.append(str(Num[0]))

#if len(matches) > 0:
#    print(''.join(matches))

Qui sort cela ....

('1', ', 343') -

Je ne sais pas pourquoi le milieu ", 234" est ignoré. Quelque chose ne va pas avec l'expression régulière, j'en suis sûr. Je n'arrive pas à envelopper ma tête autour de celui-ci.

Toute aide ou explication serait appréciée.

MODIFICATION DE SUIVI. Alors après avoir suivi tous vos conseils que j'ai pu assimiler, je l'ai fait fonctionner parfaitement pour plusieurs entrées.

import re

testStr = '1,234,343'
numComma = re.compile(r'^(?:\d{1,3})(?:,\d{3})*$')

Num = numComma.findall(testStr)
print(Num)

Donne moi....

[«1 234 343»]

Génial! MAIS! Qu'en est-il lorsque je change l'entrée de chaîne en quelque chose comme

«1 234 343 et 12 345»

Le même code renvoie ....

[]

Grrr ... lol, c'est amusant, je dois admettre.

Le but de l'exercice est donc de pouvoir éventuellement scanner un bloc de texte et sélectionner tous les nombres dans ce format. Un aperçu? Je pensais que cela ajouterait un tuple supplémentaire, pas un retour vide ...

MODIFICATION DE SUIVI:

Donc, un jour plus tard (occupé avec 3 filles et Honey-do listes), j'ai enfin pu m'asseoir et examiner toute l'aide que j'ai reçue. Voici ce que j'ai trouvé et cela semble fonctionner parfaitement. Inclus des commentaires pour ma propre compréhension personnelle. Merci encore pour tout, Blckknght, Saleem, mhawke et BHustus.

Mon code final:

import re

testStr = '12,454 So hopefully will match 1,234 and 23,322 and 1,234,567 and 12 but not 1,23,1 or ,,1111, or anything else silly.'

numComma = re.compile(r'''
    (?:(?<=^)|(?<=\s))  # Looks behind the Match for start of line and whitespace
    ((?:\d{1,3})        # Matches on groups of 1-3 numbers.
    (?:,\d{3})*)        # Matches on groups of 3 numbers preceded by a comma
    (?=\s|$)''', re.VERBOSE)    # Looks ahead of match for end of line and whitespace

Num = numComma.findall(testStr)
print(Num)

Qui retourne:

['12, 454 ',' 1,234 ', '23, 322', '1,234,567', '12']

Merci encore! J'ai eu une telle première expérience positive ici, incroyable. =)

3
Andy Moore 6 mars 2016 à 07:18

4 réponses

Meilleure réponse

Le problème est:

Une correspondance regex renverra un élément de tuple pour chaque groupe. Cependant , il est important de distinguer un groupe d'une capture . Comme vous n'avez que deux groupes séparés par des parenthèses, les correspondances seront toujours des tuples de deux: le premier groupe et le second. Mais le deuxième groupe correspond deux fois.

1: premier groupe, capturé
,234: deuxième groupe, capturé
,343: également le deuxième groupe, ce qui signifie qu'il écrase ,234.

Malheureusement , il semble que vanilla Python ne dispose d'aucun moyen d'accéder à toutes les captures d'un groupe autre que le dernier d'une manière similaire à l'implémentation regex de .NET. Cependant , si vous souhaitez uniquement obtenir le numéro spécifique, le mieux serait d'utiliser re.search(number). S'il renvoie une valeur non None, la chaîne d'entrée est un nombre valide. Sinon, ce n'est pas le cas.

De plus: Un test sur votre regex. Notez que, comme Paul Hankin l'a déclaré, les cas de test 6 et 7 correspondent même s'ils ne devraient pas, en raison du premier * suivant le premier groupe de capture, ce qui fera correspondre le groupe initial autant de fois que nécessaire. Sinon, votre expression régulière est correcte. Version fixe.

RÉPONSE À LA MODIFICATION:
La raison maintenant que votre regex retourne un ensemble vide sur 'et' est à cause des ancres ^ et $ dans votre regex. L'ancre ^, au début de l'expression régulière, indique que «ce point doit être au début d'une chaîne». Le $ est son homologue, en disant: «Cela doit être à la fin de la chaîne». C'est bien si vous voulez que votre chaîne entière du début à la fin corresponde au modèle, mais si vous voulez choisir plusieurs nombres, vous devez les supprimer.

CEPENDANT!
Si vous laissez l'expression régulière dans sa forme actuelle sans ancres, elle correspondra désormais aux éléments individuels de 1,23,45 en tant que nombres séparés. Donc, pour cela, nous devons ajouter une assertion d'anticipation positive de largeur nulle et dire: 'assurez-vous qu'après ce nombre, il y a soit des espaces ou la fin d'une ligne'. Vous pouvez voir le changement ici. L'extrémité arrière, (?=\s|$), est notre assertion d'anticipation: elle ne capture rien, mais s'assure simplement que les critères ou sont respectés, dans ce cas, les espaces (\s) ou (|) le fin d'une ligne ($).

MAIS: Dans la même veine, la regex précédente aurait correspondu à 2 dans "1234,567", nous donnant le nombre "234,567", ce qui serait mauvais. Nous utilisons donc une assertion derrière similaire à notre lookahead à la fin: (?<!^|\s), ne correspond que si au début de la chaîne ou s'il y a un espace avant le nombre. Cette version peut être trouvée ici, et devrait parfaitement satisfaire tous les besoins liés aux nombres non décimaux.

1
MutantOctopus 6 mars 2016 à 05:49

Essayer:

import re
p = re.compile(ur'(?:(?<=^)|(?<=\s))((?:\d{1,3})(?:,\d{3})*)(?=\s|$)', re.DOTALL)

test_str = """1,234 and 23,322 and 1,234,567 1,234,567,891 200 and 12 but
not 1,23,1 or ,,1111, or anything else silly"""

for m in re.findall(p, test_str):
    print m

Et sa sortie sera

  • 1 234
  • 23 322
  • 1 234 567
  • 1 234 567 891
  • 200
  • 12

Vous pouvez voir la démo ici

0
Saleem 6 mars 2016 à 05:07

Le problème est dû au fait que vous utilisez un groupe de capture répété, (,\d{3})* dans votre modèle. Le moteur d'expression régulière de Python le comparera à la fois aux milliers et aux groupes de votre numéro, mais seule la dernière répétition sera capturée.

Je soupçonne que vous préférez utiliser des groupes non capturants. Ajoutez ?: au début de chaque ensemble de parenthèses (je recommanderais également, en principe, d'utiliser une chaîne brute, bien que vous n'ayez pas de problèmes d'échappement dans votre modèle actuel):

numComma = re.compile(r'^(?:\d{1,3})(?:,\d{3})*$')

Puisqu'il n'y a aucun groupe capturé, re.findall renverra tout le texte correspondant, ce qui, je pense, est ce que vous vouliez. Vous pouvez également utiliser re.find ou re.search et appeler la méthode group() sur l'objet match renvoyé pour obtenir l'intégralité du texte correspondant.

4
Blckknght 6 mars 2016 à 04:39

Cette expression régulière correspondrait à n'importe quel nombre valide et ne correspondrait jamais à un nombre non valide:

(?<=^|\s)(?:(?:0|[1-9][0-9]{0,2}(?:,[0-9]{3})*))(?=\s|$)

https://regex101.com/r/dA4yB1/1

0
matsib.dev 6 mars 2016 à 06:17