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].


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+).*

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

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é.


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.


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);
}

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 = "adf@gmail.com, <another@osiem.osiem>>>> lalala@aaa.pl"cela va créer une liste de 3 éléments.


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));
}

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);

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)


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.


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]

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);
}

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());
         }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.