Comment extraire des nombres d'une chaîne et obtenir un tableau d'entiers?


109

J'ai une variable String (essentiellement une phrase en anglais avec un nombre non spécifié de nombres) et j'aimerais extraire tous les nombres dans un tableau d'entiers. Je me demandais s'il y avait une solution rapide avec les expressions régulières?


J'ai utilisé la solution de Sean et l'ai légèrement modifiée:

LinkedList<String> numbers = new LinkedList<String>();

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(line); 
while (m.find()) {
   numbers.add(m.group());
}

1
Les nombres sont-ils entourés d'espaces ou d'autres caractères? Comment les nombres sont-ils formatés, sont-ils hexadécimaux, octaux, binaires, décimaux?
Buhake Sindi

Je pensais que c'était clair d'après la question: c'est une phrase en anglais avec des chiffres. De plus, je parlais d'un tableau d'entiers, donc ce que je cherchais étaient des entiers.
John Manak

Réponses:


175
Pattern p = Pattern.compile("-?\\d+");
Matcher m = p.matcher("There are more than -2 and less than 12 numbers here");
while (m.find()) {
  System.out.println(m.group());
}

... imprime -2et 12.


-? correspond à un signe négatif de premier plan - éventuellement. \ d correspond à un chiffre, et nous devons cependant écrire \comme \\dans une chaîne Java. Ainsi, \ d + correspond à 1 ou plusieurs chiffres.


4
Pourriez-vous compléter votre réponse en expliquant votre expression régulière s'il vous plaît?
OscarRyz

3
-? correspond à un signe négatif de premier plan - éventuellement. \ d correspond à un chiffre, et nous devons cependant écrire \ as \\ dans une chaîne Java. Donc, \\ d + correspond à 1 plus de chiffres
Sean Owen

7
J'ai changé mon expression en Pattern.compile ("-? [\\ d \\.] +") Pour prendre en charge les flottants. Vous me conduisez définitivement sur le chemin, Thx!
jlengrand

Cette méthode détecte les chiffres mais ne détecte pas les nombres formatés, par exemple 2,000. Pour une telle utilisation-?\\d+,?\\d+|-?\\d+
Mugoma J.Okomba

Cela ne prend en charge qu'une seule virgule, donc manquerait "2 000 000". Il accepte également des chaînes comme "2,00". Si les séparateurs par virgule doivent être pris en charge, alors: -?\\d+(,\\d{3})*devrait fonctionner.
Sean Owen

52

Qu'en est-il de l'utilisation de la replaceAllméthode java.lang.String:

    String str = "qwerty-1qwerty-2 455 f0gfg 4";      
    str = str.replaceAll("[^-?0-9]+", " "); 
    System.out.println(Arrays.asList(str.trim().split(" ")));

Production:

[-1, -2, 455, 0, 4]

La description

[^-?0-9]+
  • [et ]délimite un ensemble de caractères à mettre en correspondance, c'est-à-dire une seule fois dans n'importe quel ordre
  • ^Identifiant spécial utilisé au début de l'ensemble, utilisé pour indiquer de faire correspondre tous les caractères non présents dans l'ensemble délimité, au lieu de tous les caractères présents dans l'ensemble.
  • + Entre un et des temps illimités, autant de fois que possible, en redonnant au besoin
  • -? Un des caractères «-» et «?»
  • 0-9 Un caractère compris entre «0» et «9»

4
Pourquoi voudriez-vous garder des points d'interrogation? En outre, cette traite -par lui - même comme un nombre, ainsi que des choses comme 9-, ---6et 1-2-3.
Alan Moore

1
Une très belle alternative sans utiliser de bibliothèques d'importation;)
Jcc.Sanabria

18
Pattern p = Pattern.compile("[0-9]+");
Matcher m = p.matcher(myString);
while (m.find()) {
    int n = Integer.parseInt(m.group());
    // append n to list
}
// convert list to array, etc

Vous pouvez en fait remplacer [0-9] par \ d, mais cela implique un échappement de double barre oblique inverse, ce qui rend la lecture plus difficile.


Oups. Sean's gère les nombres négatifs, c'est donc une amélioration.
sidéral

2
le vôtre gérera également les nombres négatifs si vous utilisez "-? [0-9] +"
cegprakash

9
  StringBuffer sBuffer = new StringBuffer();
  Pattern p = Pattern.compile("[0-9]+.[0-9]*|[0-9]*.[0-9]+|[0-9]+");
  Matcher m = p.matcher(str);
  while (m.find()) {
    sBuffer.append(m.group());
  }
  return sBuffer.toString();

Ceci est pour extraire des nombres en conservant la décimale


Ne gère pas les négatifs
OneCricketeer

5

La réponse acceptée détecte les chiffres mais ne détecte pas les nombres formatés, par exemple 2 000, ni les décimales, par exemple 4,8. Pour une telle utilisation -?\\d+(,\\d+)*?\\.?\\d+?:

        Pattern p = Pattern.compile("-?\\d+(,\\d+)*?\\.?\\d+?");
        List<String> numbers = new ArrayList<String>();
        Matcher m = p.matcher("Government has distributed 4.8 million textbooks to 2,000 schools");
        while (m.find()) {  
            numbers.add(m.group());
        }   
        System.out.println(numbers);

Production: [4.8, 2,000]


1
@JulienS .: Je ne suis pas d'accord. Cette regex fait bien plus que ce que l'OP demandait, et ce n'est pas le cas. (Au moins, la partie décimale devrait être dans un groupe facultatif, avec tout ce qui est requis et gourmand:. (?:\.\d+)?)
Alan Moore

Vous avez certainement un point ici pour la partie décimale. Cependant, il est très courant de rencontrer des nombres formatés.
Julien

@AlanMo De plus en plus de visiteurs de SO recherchent des moyens différents pour résoudre des problèmes avec des similitudes / différences variables, et il est utile que des suggestions soient faites. Même le PO aurait pu être simplifié à l'extrême.
Mugoma J.Okomba

4

pour les nombres rationnels, utilisez celui-ci: (([0-9]+.[0-9]*)|([0-9]*.[0-9]+)|([0-9]+))


1
L'OP dit des nombres entiers, pas des nombres réels. De plus, vous avez oublié d'échapper aux points, et aucune de ces parenthèses n'est nécessaire.
Alan Moore

3

En utilisant Java 8, vous pouvez faire:

String str = "There 0 are 1 some -2-34 -numbers 567 here 890 .";
int[] ints = Arrays.stream(str.replaceAll("-", " -").split("[^-\\d]+"))
                 .filter(s -> !s.matches("-?"))
                 .mapToInt(Integer::parseInt).toArray();
System.out.println(Arrays.toString(ints)); // prints [0, 1, -2, -34, 567, 890]

Si vous n'avez pas de nombres négatifs, vous pouvez vous débarrasser de replaceAll(et utiliser !s.isEmpty()dans filter), car c'est seulement pour diviser correctement quelque chose comme 2-34(cela peut également être géré uniquement avec regex in split, mais c'est assez compliqué).

Arrays.streamtransforme notre String[]en un Stream<String>.

filter se débarrasse des chaînes vides de début et de fin ainsi que de tout - qui ne font pas partie d'un nombre.

mapToInt(Integer::parseInt).toArray()appelle parseIntchacun Stringà nous donner un int[].


Alternativement, Java 9 a une méthode Matcher.results , qui devrait permettre quelque chose comme:

Pattern p = Pattern.compile("-?\\d+");
Matcher m = p.matcher("There 0 are 1 some -2-34 -numbers 567 here 890 .");
int[] ints = m.results().map(MatchResults::group).mapToInt(Integer::parseInt).toArray();
System.out.println(Arrays.toString(ints)); // prints [0, 1, -2, -34, 567, 890]

Dans l'état actuel des choses, ni l'un ni l'autre ne représente une grande amélioration par rapport à la simple lecture en boucle des résultats avec Pattern/ Matchercomme indiqué dans les autres réponses, mais cela devrait être plus simple si vous souhaitez suivre cela avec des opérations plus complexes qui sont considérablement simplifiées avec l'utilisation de ruisseaux.


1

Extrayez tous les nombres réels en utilisant ceci.

public static ArrayList<Double> extractNumbersInOrder(String str){

    str+='a';
    double[] returnArray = new double[]{};

    ArrayList<Double> list = new ArrayList<Double>();
    String singleNum="";
    Boolean numStarted;
    for(char c:str.toCharArray()){

        if(isNumber(c)){
            singleNum+=c;

        } else {
            if(!singleNum.equals("")){  //number ended
                list.add(Double.valueOf(singleNum));
                System.out.println(singleNum);
                singleNum="";
            }
        }
    }

    return list;
}


public static boolean isNumber(char c){
    if(Character.isDigit(c)||c=='-'||c=='+'||c=='.'){
        return true;
    } else {
        return false;
    }
}

1

Les fractions et les caractères de regroupement pour représenter les nombres réels peuvent différer selon les langues. Le même nombre réel peut être écrit de manière très différente selon la langue.

Le nombre deux millions en allemand

2 000 000,00

et en anglais

2.000.000,00

Une méthode pour extraire complètement les nombres réels d'une chaîne donnée d'une manière indépendante du langage:

public List<BigDecimal> extractDecimals(final String s, final char fraction, final char grouping) {
    List<BigDecimal> decimals = new ArrayList<BigDecimal>();
    //Remove grouping character for easier regexp extraction
    StringBuilder noGrouping = new StringBuilder();
    int i = 0;
    while(i >= 0 && i < s.length()) {
        char c = s.charAt(i);
        if(c == grouping) {
            int prev = i-1, next = i+1;
            boolean isValidGroupingChar =
                    prev >= 0 && Character.isDigit(s.charAt(prev)) &&
                    next < s.length() && Character.isDigit(s.charAt(next));                 
            if(!isValidGroupingChar)
                noGrouping.append(c);
            i++;
        } else {
            noGrouping.append(c);
            i++;
        }
    }
    //the '.' character has to be escaped in regular expressions
    String fractionRegex = fraction == POINT ? "\\." : String.valueOf(fraction);
    Pattern p = Pattern.compile("-?(\\d+" + fractionRegex + "\\d+|\\d+)");
    Matcher m = p.matcher(noGrouping);
    while (m.find()) {
        String match = m.group().replace(COMMA, POINT);
        decimals.add(new BigDecimal(match));
    }
    return decimals;
}

1

Si vous souhaitez exclure des nombres contenus dans des mots, tels que bar1 ou aa1bb, ajoutez des limites de mot \ b à l'une des réponses basées sur les regex. Par exemple:

Pattern p = Pattern.compile("\\b-?\\d+\\b");
Matcher m = p.matcher("9There 9are more9 th9an -2 and less than 12 numbers here9");
while (m.find()) {
  System.out.println(m.group());
}

affiche:

2
12

1

Je suggérerais de vérifier les valeurs ASCII pour extraire les nombres d'une chaîne Supposons que vous ayez une chaîne d'entrée en tant que myname12345 et si vous souhaitez simplement extraire les nombres 12345, vous pouvez le faire en convertissant d'abord la chaîne en tableau de caractères, puis utilisez le pseudocode suivant

    for(int i=0; i < CharacterArray.length; i++)
    {
        if( a[i] >=48 && a[i] <= 58)
            System.out.print(a[i]);
    }

une fois les nombres extraits, ajoutez-les à un tableau

J'espère que cela t'aides


Une chaîne Java est une séquence comptée d'unités de code Unicode / UTF-16. De par la conception de l'UTF-16, les 128 premiers caractères ont la même valeur (mais pas la même taille) que leur codage ASCII; Au-delà de cela, penser que vous avez affaire à l'ASCII entraînera des erreurs.
Tom Blodget

0

J'ai trouvé cette expression la plus simple

String[] extractednums = msg.split("\\\\D++");

-1
public static String extractNumberFromString(String number) {
    String num = number.replaceAll("[^0-9]+", " ");
    return num.replaceAll(" ", "");
}

extrait uniquement les nombres de la chaîne

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.