Utilisation d'expressions régulières pour extraire une valeur en Java

169

J'ai plusieurs chaînes sous la forme approximative:

[some text] [some number] [some more text]

Je veux extraire le texte en [un certain nombre] en utilisant les classes Java Regex.

Je sais à peu près quelle expression régulière je veux utiliser (bien que toutes les suggestions soient les bienvenues). Ce qui m'intéresse vraiment, ce sont les appels Java pour prendre la chaîne regex et l'utiliser sur les données source pour produire la valeur de [un certain nombre].

EDIT: Je dois ajouter que je ne suis intéressé que par un seul [un certain nombre] (en gros, la première instance). Les chaînes source sont courtes et je ne vais pas chercher plusieurs occurrences de [un certain nombre].

Craig Walker
la source
11
... et maintenant je pars en recherche. Voyons si SO peut obtenir une réponse pour moi avant que je ne le découvre moi-même. :-P
Craig Walker
c'était une question d'entrevue dans une société bancaire / d'investissement / commerciale pour le génie logiciel, n'est-ce pas? : P
ennth
@ennth Non, même pas proche! C'était pour le code de production sur un site Web de petite entreprise ... il y a plusieurs lunes.
Craig Walker
1
sacrément bien, on m'a posé presque la même question exacte lors d'un examen de codage de génie logiciel JP Morgan Chase il y a quelques jours à peine: P
ennth

Réponses:

316

Exemple complet:

private static final Pattern p = Pattern.compile("^([a-zA-Z]+)([0-9]+)(.*)");
public static void main(String[] args) {
    // create matcher for pattern p and given string
    Matcher m = p.matcher("Testing123Testing");

    // if an occurrence if a pattern was found in a given string...
    if (m.find()) {
        // ...then you can use group() methods.
        System.out.println(m.group(0)); // whole matched expression
        System.out.println(m.group(1)); // first expression from round brackets (Testing)
        System.out.println(m.group(2)); // second one (123)
        System.out.println(m.group(3)); // third one (Testing)
    }
}

Puisque vous recherchez le premier nombre, vous pouvez utiliser une telle expression rationnelle:

^\D+(\d+).*

et m.group(1)vous renverra le premier numéro. Notez que les nombres signés peuvent contenir un signe moins:

^\D+(-?\d+).*
Allain Lalonde
la source
62
N'oubliez pas de réutiliser l'objet Patter. La compilation des motifs prend énormément de temps.
Rastislav Komara
14
D'accord. Habituellement, je définirais le modèle comme un modèle final statique privé PATTERN = Pattern.compile ("..."); Mais ce n'est que moi.
Allain Lalonde
6
nous pouvons simplement utiliser Pattern p = Pattern.compile ("\\ d +");
javaMan
15
Sans explication, c'est une mauvaise réponse.
Martin Spamer
Vous pouvez également réutiliser le Matcher. Appelez la méthode reset () du Matcher entre chaque utilisation. Si vous partagez le matcher sur plusieurs threads simultanés, vous devez synchroniser l'opération.
Marquez
41
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex1 {
    public static void main(String[]args) {
        Pattern p = Pattern.compile("\\d+");
        Matcher m = p.matcher("hello1234goodboy789very2345");
        while(m.find()) {
            System.out.println(m.group());
        }
    }
}

Production:

1234
789
2345
javaMan
la source
La question ne demande spécifiquement que la PREMIÈRE occurrence de nombres.
NoBrainer
34

Allain a essentiellement le code java, vous pouvez donc l'utiliser. Cependant, son expression ne correspond que si vos nombres sont uniquement précédés d'un flux de caractères de mots.

"(\\d+)"

devrait pouvoir trouver la première chaîne de chiffres. Vous n'avez pas besoin de spécifier ce qui précède, si vous êtes sûr que ce sera la première chaîne de chiffres. De même, il ne sert à rien de spécifier ce qui se trouve après, sauf si vous le souhaitez. Si vous voulez juste le numéro et êtes sûr que ce sera la première chaîne d'un ou plusieurs chiffres, c'est tout ce dont vous avez besoin.

Si vous vous attendez à ce qu'il soit décalé par des espaces, cela le rendra encore plus distinct à spécifier

"\\s+(\\d+)\\s+"

pourrait être mieux.

Si vous avez besoin des trois parties, cela fera l'affaire:

"(\\D+)(\\d+)(.*)"

MODIFIER Les expressions données par Allain et Jack suggèrent que vous devez spécifier un sous-ensemble de non-chiffres afin de capturer des chiffres . Si vous dites au moteur d'expression régulière que vous recherchez, \dil ignorera tout avant les chiffres. Si l'expression de J ou A correspond à votre modèle, alors la correspondance entière est égale à la chaîne d'entrée . Et il n'y a aucune raison de le spécifier. Cela ralentit probablement un match propre, s'il n'est pas totalement ignoré.

Axeman
la source
vous pouvez tester l'hypothèse d'Axemans en exécutant un exemple de test et en vérifiant les performances de sa solution par rapport à A / J.
anjanb
Vous n'avez pas besoin de spécifier le début et la fin de la chaîne. Sinon, des choses comme 124xxx123xxx seraient mises en correspondance même si elles ne correspondent pas à sa syntaxe? Ou est-ce que ^ et $ sont implicites?
Allain Lalonde
Allain, le vôtre échouerait aussi. Vous et Jack supposez que les caractères non numériques précéderont les chiffres. Soit ils le font, soit ils ne le font pas. Dans ce cas, aucune de ces expressions n'analysera cette ligne. Je répète que, comme spécifié , le modèle pour les chiffres est suffisant.
Axeman
11

En plus de Pattern , la classe Java String a également plusieurs méthodes qui peuvent fonctionner avec des expressions régulières, dans votre cas, le code sera:

"ab123abc".replaceFirst("\\D*(\\d*).*", "$1")

\\Dest un caractère non numérique.

Vitalii Fedorenko
la source
10

Dans Java 1.4 et plus:

String input = "...";
Matcher matcher = Pattern.compile("[^0-9]+([0-9]+)[^0-9]+").matcher(input);
if (matcher.find()) {
    String someNumberStr = matcher.group(1);
    // if you need this to be an int:
    int someNumberInt = Integer.parseInt(someNumberStr);
}
Jack Leow
la source
8

Cette fonction collecte toutes les séquences correspondantes de string. Dans cet exemple, toutes les adresses e-mail proviennent de string.

static final String EMAIL_PATTERN = "[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
        + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})";

public List<String> getAllEmails(String message) {      
    List<String> result = null;
    Matcher matcher = Pattern.compile(EMAIL_PATTERN).matcher(message);

    if (matcher.find()) {
        result = new ArrayList<String>();
        result.add(matcher.group());

        while (matcher.find()) {
            result.add(matcher.group());
        }
    }

    return result;
}

Pour message = "[email protected], <[email protected]>>>> [email protected]"cela va créer une liste de 3 éléments.

LukaszTaraszka
la source
3

Essayez de faire quelque chose comme ceci:

Pattern p = Pattern.compile("^.+(\\d+).+");
Matcher m = p.matcher("Testing123Testing");

if (m.find()) {
    System.out.println(m.group(1));
}
Tint Naing Win
la source
3
-1. Parce que .+consomme avidement les personnages, \d+ne capture que le à "3"partir de "123". De plus, dans les littéraux de chaîne, vous devez échapper la barre oblique inverse (votre exemple ne sera pas compilé).
Bart Kiers
3

Solution simple

// Regexplanation:
// ^       beginning of line
// \\D+    1+ non-digit characters
// (\\d+)  1+ digit characters in a capture group
// .*      0+ any character
String regexStr = "^\\D+(\\d+).*";

// Compile the regex String into a Pattern
Pattern p = Pattern.compile(regexStr);

// Create a matcher with the input String
Matcher m = p.matcher(inputStr);

// If we find a match
if (m.find()) {
    // Get the String from the first capture group
    String someDigits = m.group(1);
    // ...do something with someDigits
}

Solution dans une classe Util

public class MyUtil {
    private static Pattern pattern = Pattern.compile("^\\D+(\\d+).*");
    private static Matcher matcher = pattern.matcher("");

    // Assumptions: inputStr is a non-null String
    public static String extractFirstNumber(String inputStr){
        // Reset the matcher with a new input String
        matcher.reset(inputStr);

        // Check if there's a match
        if(matcher.find()){
            // Return the number (in the first capture group)
            return matcher.group(1);
        }else{
            // Return some default value, if there is no match
            return null;
        }
    }
}

...

// Use the util function and print out the result
String firstNum = MyUtil.extractFirstNumber("Testing4234Things");
System.out.println(firstNum);
NoBrainer
la source
1

Regardez, vous pouvez le faire en utilisant StringTokenizer

String str = "as:"+123+"as:"+234+"as:"+345;
StringTokenizer st = new StringTokenizer(str,"as:");

while(st.hasMoreTokens())
{
  String k = st.nextToken();    // you will get first numeric data i.e 123
  int kk = Integer.parseInt(k);
  System.out.println("k string token in integer        " + kk);

  String k1 = st.nextToken();   //  you will get second numeric data i.e 234
  int kk1 = Integer.parseInt(k1);
  System.out.println("new string k1 token in integer   :" + kk1);

  String k2 = st.nextToken();   //  you will get third numeric data i.e 345
  int kk2 = Integer.parseInt(k2);
  System.out.println("k2 string token is in integer   : " + kk2);
}

Puisque nous prenons ces données numériques dans trois variables différentes, nous pouvons utiliser ces données n'importe où dans le code (pour une utilisation ultérieure)

shounak
la source
0

Que diriez-vous [^\\d]*([0-9]+[\\s]*[.,]{0,1}[\\s]*[0-9]*).*je pense qu'il prendrait soin des nombres avec partie fractionnaire. J'ai inclus des espaces blancs et inclus ,comme séparateur possible. J'essaie d'obtenir les nombres d'une chaîne comprenant des flottants et en tenant compte du fait que l'utilisateur peut faire une erreur et inclure des espaces blancs lors de la saisie du nombre.

arturo
la source
0

Parfois, vous pouvez utiliser la méthode .split ("REGEXP") simple disponible dans java.lang.String. Par exemple:

String input = "first,second,third";

//To retrieve 'first' 
input.split(",")[0] 
//second
input.split(",")[1]
//third
input.split(",")[2]
utilisateur1722707
la source
0
Pattern p = Pattern.compile("(\\D+)(\\d+)(.*)");
Matcher m = p.matcher("this is your number:1234 thank you");
if (m.find()) {
    String someNumberStr = m.group(2);
    int someNumberInt = Integer.parseInt(someNumberStr);
}
Mohammadreza Tavakoli
la source
1
Veuillez modifier avec plus d'informations. Les réponses à base de code uniquement et "essayez ceci" sont déconseillées, car elles ne contiennent aucun contenu interrogeable et n'expliquent pas pourquoi quelqu'un devrait "essayer ceci". Nous nous efforçons ici d'être une ressource de connaissance.
Brian Tompsett - 汤 莱恩
1
Downvoter pour simplement répéter les bonnes réponses qui ont été données il y a longtemps sans ajouter de valeur supplémentaire
Forage
-1

si vous lisez un fichier, cela peut vous aider

              try{
             InputStream inputStream = (InputStream) mnpMainBean.getUploadedBulk().getInputStream();
             BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
             String line;
             //Ref:03
             while ((line = br.readLine()) != null) {
                if (line.matches("[A-Z],\\d,(\\d*,){2}(\\s*\\d*\\|\\d*:)+")) {
                     String[] splitRecord = line.split(",");
                     //do something
                 }
                 else{
                     br.close();
                     //error
                     return;
                 }
             }
                br.close();

             }
         }
         catch (IOException  ioExpception){
             logger.logDebug("Exception " + ioExpception.getStackTrace());
         }
chercheur
la source