2009-11-11 5 views
3

J'ai une chaîne comme la suivante:Extraire deux chiffres d'une chaîne

« une certaine valeur est 25, mais ne doit pas être plus grand que 12 »

Je veux extraire les deux chiffres de la chaîne.

Les nombres sont des nombres entiers.

Il se peut qu'il n'y ait pas de texte avant le premier chiffre et du texte après le second nombre.

J'ai essayé de le faire avec une expression rationnelle et les groupes, mais a lamentablement échoué:

public MessageParser(String message) { 
    Pattern stringWith2Numbers = Pattern.compile(".*(\\d?).*(\\d?).*"); 
    Matcher matcher = stringWith2Numbers.matcher(message); 
    if (!matcher.matches()) { 
     couldParse = false; 
     firstNumber = 0; 
     secondNumber = 0; 
    } else { 
     final String firstNumberString = matcher.group(1); 
     firstNumber = Integer.valueOf(firstNumberString); 
     final String secondNumberString = matcher.group(2); 
     secondNumber = Integer.valueOf(secondNumberString); 

     couldParse = true; 
    } 
} 

Toute aide est aprécié.

Répondre

3

Vos modèles ".*" sont gourmands, comme c'est leur habitude, et ils engloutissent autant qu'ils le peuvent - ce qui va être la chaîne entière. Alors que le premier ".*" correspond à la chaîne entière, rendant le reste discutable. En outre, vos clauses "\\d?" indiquent un seul chiffre qui s'avère être facultatif, ce qui n'est pas ce que vous voulez.

Ceci est probablement plus conforme à ce que vous filmez pour:

Pattern stringWith2Numbers = Pattern.compile(".*?(\\d+).*?(\\d+).*?");

Bien sûr, puisque vous ne se soucient pas vraiment les choses avant ou après les chiffres, pourquoi s'embêter avec eux ?

Pattern stringWith2Numbers = Pattern.compile("(\\d+).*?(\\d+)");

Cela devrait faire l'affaire. En prenant le temps d'écrire des bandes dessinées géniales, Alan Moore a signalé quelques problèmes avec ma solution dans les commentaires. Pour commencer, si vous n'avez qu'un seul nombre à plusieurs chiffres dans la chaîne, ma solution se trompe. L'appliquer à "Cette 123 est une mauvaise chaîne" l'amènerait à retourner "12" et "3" quand il devrait simplement échouer.Une meilleure expression rationnelle ne stipule qu'il doit y avoir au moins un caractère non numérique qui sépare les deux nombres, comme suit:

Pattern stringWith2Numbers = Pattern.compile("(\\d+)\\D+(\\d+)");

En outre, matches() applique le motif de la chaîne entière , essentiellement bracketing en ^ et $; find() ferait l'affaire, mais ce n'est pas ce que l'OP utilisait. Donc coller avec matches(), nous aurions besoin de ramener ces clauses "inutiles" devant et après les deux nombres. (Bien que les ayant correspondent explicitement non-chiffres au lieu du caractère générique est meilleure forme.) Il ressemblerait à ceci:

Pattern stringWith2Numbers = Pattern.compile("\\D*(\\d+)\\D+(\\d+)\\D*");

... qui, il faut noter, est sacrément près identique à la réponse de jjnguy .

+0

Est-ce que le ". *" Entre les modèles de nombres "mange" le deuxième nombre? – MartinStettner

+1

Non. Le point d'interrogation venant après l'étoile indique qu'il doit correspondre à la chaîne la plus courte possible - ainsi, il correspondra à tout avant le second nombre. – BlairHippo

+0

... bien que, parce que les clauses à deux chiffres ne sont plus facultatives, le point d'interrogation est moins important - si vous êtes absolument certain qu'il y aura deux et SEULEMENT deux nombres dans la chaîne, vous n'en avez pas besoin. Cela importe s'il y a plus de chiffres, cependant. En utilisant "1 et 2 et 3" comme un exemple de chaîne: avec le point d'interrogation, vous obtenez 1 et 2 pêché. Sans, 1 et 3 seraient les deux valeurs extraites. – BlairHippo

8

Votre modèle devrait ressembler davantage:

Pattern stringWith2Numbers = Pattern.compile("\\D*(\\d+)\\D+(\\d+)\\D*"); 

Vous devez accepter \\d+ car il peut être un ou plusieurs chiffres.

+0

Comme indiqué ci-dessous, le ". *" "Mange" toute la chaîne ... – MartinStettner

+0

\ D * (\ d +)^\ D] * (\ d +)^\ D * ne serait pas légèrement plus approprié? Comme nous ne voulons explicitement pas de chiffres et. a le potentiel de correspondre à un chiffre avant d'arriver à \ d. –

+0

Je déteste les expressions régulières, merci. – jjnguy

2

Votre regex correspond, mais tout est mangé par votre premier .* et le reste correspond à la chaîne vide. Remplacez votre expression régulière par "\\D*(\\d+)\\D+(\\d+)\\D*". Ceci devrait se lire comme suit: Au moins un chiffre suivi d'au moins un caractère qui n'est pas un chiffre, suivi d'au moins un chiffre. Page 8

+2

Les points de début et de fin '. *' Sont nécessaires si vous utilisez la méthode 'matches()' comme le fait l'OP. Votre regex fonctionnera avec la méthode 'find()', qui effectue le type de regex correspondant le plus traditionnel: «c'est là quelque part». –

+0

Merci pour la clarification Alan, j'ai édité ma réponse. –

Questions connexes