2010-04-17 4 views
1

Ceci est une question regex vraiment basique mais comme je n'arrive pas à comprendre pourquoi la correspondance échoue dans certaines circonstances, je me suis dit que je la posterais pour voir si quelqu'un d'autre peut signaler ce qui me manque.Pourquoi mon regex échoue lorsque le nombre se termine par 0?

Je suis en train de sortir les 2 séries de chiffres à partir de chaînes de la forme:

12309123098_102938120938120938 
1321312_103810312032123 
123123123_10983094854905490 
38293827_1293120938129308 

J'utilise le code suivant pour traiter chaque chaîne:

if($string && $string =~ /^(\d)+_(\d)+$/) { 
    if(IsInteger($1) && IsInteger($2)) { print "success ('$1','$2')"; } 
    else { print "fail"; } 
} 

Lorsque la fonction IsInterger() est la suivante:

sub IsInteger { 
    my $integer = shift; 
    if($integer && $integer =~ /^\d+$/) { return 1; } 
    return; 
} 

Cette fonction semble fonctionner la plupart du temps, mais échoue sur le followi ng pour une raison quelconque:

1287123437_1268098784380 
1287123437_1267589971660 

Avez-vous des idées sur les raisons pour lesquelles ces solutions échouent alors que d'autres réussissent? Merci d'avance pour votre aide!

+5

pour quoi avez besoin fonction EstEntier vous? Votre première regex ne mettra qu'une séquence de chiffres à '$ 1' et '$ 2' pas besoin de tester cela. Remplacez tout le 'if' avec' if ($ string = ~/^ (\ d +) _ (\ d +) /) {print "succès ('$ 1', '$ 2')"; } else {print "échec"; } ' – ZyX

+1

Et pourquoi écrivez-vous' if ($ string) '? Tu n'as pas besoin de ça. – ZyX

+0

@ZyZ - bon point. Je suis allé de l'avant et mettre à jour la fonction. –

Répondre

3

Ceci est un add-on aux réponses de Unicornaddict et ZyX: qu'est-ce que vous essayez de faire correspondre?

Si vous essayez de faire correspondre les séquences gauche et droite de '_', le dépendant de la licorne est correct et votre expression régulière doit être ^(\d+)_(\d+)$. En outre, vous pouvez vous débarrasser de la première qualification et la fonction « IsIntrger()` tout à fait - vous le savez déjà est un entier - il correspond (\ d +)

if ($string =~ /^(\d+)_(\d+)$/) { 
    print "success ('$1','$2')"; 
} else { 
    print "fail\n"; 
} 

Si vous essayez de faire correspondre le dernier chiffre dans chacun et se demandant pourquoi il échoue, c'est le premier contrôle IsInteger() (if($intger &&). De toute façon, il est redondant (vous savez que c'est un nombre entier) et échoue à 0 car, comme le note ZyX, il est évalué à false.

Même chose applique cependant:

if ($string =~ /^(\d)+_(\d)+$/) { 
    print "success ('$1','$2')"; 
} else { 
    print "fail\n"; 
} 

Affichera success ('8','8') compte tenu de l'entrée 12309123098_102938120938120938

0

Ne devrait pas + être inclus dans le groupe:

^(\d+)_(\d+)$ au lieu de ^(\d)+_(\d)+$

3

Parce que vous avez 0 à la fin de la deuxième chaîne, (\d)+ met seulement le dernier match à la variable $N, chaîne "0" est équivalent à faux.

+0

@ZyX - bonne explication de pourquoi cela échouait. Merci de m'avoir aidé à comprendre le problème! –

3

En cas de doute, vérifiez ce que votre regex capture réellement.

use strict; 
use warnings; 

my @data = (
    '1321312_103810312032123', 
    '123123123_10983094854905490', 
); 

for my $s (@data){ 
    print "\$1=$1 \$2=$2\n" if $s =~ /^(\d)+_(\d)+$/; 
    # Output: 
    # $1=2 $2=3 
    # $1=3 $2=0 
} 

Vous avez probablement prévu la deuxième de ces deux approches.

(\d)+ # Repeat a regex group 1+ times, 
     # capturing only the last instance. 

(\d+) # Capture 1+ digits. 

En outre, à la fois dans votre boucle principale et IsInteger (qui ne semble pas nécessaire, compte tenu de la regex initiale dans la boucle principale), vous testez la vérité plutôt que quelque chose de plus spécifique, comme defined ou length. Zéro, par exemple, est un entier valide mais faux.

0

Beaucoup de gens ont commenté votre regex, mais le problème que vous aviez dans votre IsInteger (dont vous n'avez vraiment pas besoin pour votre exemple). Vous avez vérifié pour « la vérité » quand vous voulez vraiment vérifier defined:

sub IsInteger { 
    my $integer = shift; 
    if(defined $integer && $integer =~ /^\d+$/) { return 1; } 
    return; 
} 

Vous n'avez pas besoin la plupart des infrastructures dans ce sous-programme si:

sub IsInteger { 
    defined $_[0] && $_[0] =~ /^\d+$/ 
} 
+0

+1 pour défini, -1 pour la dernière partie: Tandis que vous êtes techniquement correct, le déballage de l'argument sous-programme rend le code beaucoup plus clair et je ne pense pas que ce soit une bonne idée de décourager les bonnes pratiques quand Perl est déjà difficile assez pour lire pour les non-initiés comme c'est. –

+0

Le déballage des arguments de sous-routine ne garantit pas toujours un code plus propre ou meilleur. C'est l'un des cas où vous n'obtenez aucune valeur supplémentaire. Vous faites plus de travail réel lorsque vous n'en avez pas besoin, et vous en créez plus pour lire. Si vous connaissez Perl, rien dans ma version n'est mystérieux. Si vous ne connaissez pas Perl, lisez l'un de mes livres :) –

Questions connexes