Jak wyodrębnić liczby z łańcucha i uzyskać tablicę liczb całkowitych?

109

Mam zmienną typu String (w zasadzie zdanie angielskie z nieokreśloną liczbą liczb) i chciałbym wyodrębnić wszystkie liczby do tablicy liczb całkowitych. Zastanawiałem się, czy istnieje szybkie rozwiązanie z wyrażeniami regularnymi?


Użyłem rozwiązania Seana i nieznacznie je zmieniłem:

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

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(line); 
while (m.find()) {
   numbers.add(m.group());
}
John Manak
źródło
1
Czy liczby są otoczone spacjami lub innymi znakami? Jak formatowane są liczby, czy są szesnastkowe, ósemkowe, binarne, dziesiętne?
Buhake Sindi
Pomyślałem, że jasno wynika z pytania: to angielskie zdanie z liczbami. Ponadto mówiłem o tablicy liczb całkowitych, więc szukałem liczb całkowitych.
John Manak

Odpowiedzi:

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

... wydruki -2i 12.


-? dopasowuje wiodący znak minus - opcjonalnie. \ d dopasowuje cyfrę, ale musimy pisać \tak, jak \\w Java String. Zatem \ d + dopasowuje 1 lub więcej cyfr.

Sean Owen
źródło
4
Czy mógłbyś uzupełnić swoją odpowiedź, wyjaśniając wyrażenie regularne?
OscarRyz
3
-? dopasowuje wiodący znak minus - opcjonalnie. \ d dopasowuje cyfrę i musimy jednak zapisać \ as \\ w Java String. Tak więc \\ d + pasuje do 1 więcej cyfr
Sean Owen
7
Zmieniłem swoje wyrażenie na Pattern.compile („-? [\\ d \\.] +”), Aby obsługiwać liczby zmiennoprzecinkowe. Zdecydowanie prowadzisz mnie po drodze, Thx!
jlengrand
Ta metoda wykrywa cyfry, ale nie wykrywa sformatowanych liczb, np 2,000. Do takiego użytku-?\\d+,?\\d+|-?\\d+
Mugoma J. Okomba
Obsługuje tylko jeden przecinek, więc pominięto by „2 000 000”. Akceptuje również ciągi, takie jak „2,00”. Jeśli separatory przecinków muszą być obsługiwane, to: -?\\d+(,\\d{3})*powinno działać.
Sean Owen
52

A co z replaceAllmetodą java.lang.String:

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

Wynik:

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

Opis

[^-?0-9]+
  • [i ]ogranicza zestaw znaków do pojedynczego dopasowania, tj. tylko jeden raz w dowolnej kolejności
  • ^Specjalny identyfikator używany na początku zestawu, służący do wskazania braku dopasowania wszystkich znaków występujące na wyznaczonym zestawie, zamiast przedstawić wszystkie znaki w zestawie.
  • + Od jednego do nieograniczonej liczby razy, tyle razy, ile to możliwe, oddając w razie potrzeby
  • -? Jeden ze znaków „-” i „?”
  • 0-9 Znak z zakresu od „0” do „9”
Maxim Shoustin
źródło
4
Dlaczego chcesz zachować znaki zapytania? Również ten traktuje -samodzielnie jako liczba, wraz z rzeczy, jak 9-, ---6i 1-2-3.
Alan Moore
1
Bardzo fajna alternatywa bez korzystania z importowania bibliotek;)
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

W rzeczywistości możesz zamienić [0-9] na \ d, ale wiąże się to ze znakami ucieczki z podwójnym ukośnikiem odwrotnym, co utrudnia odczytanie.

gwiezdny
źródło
Ups. Sean obsługuje liczby ujemne, więc to poprawa.
gwiazdorstwo
2
twoja poradzi sobie również z liczbami ujemnymi, jeśli użyjesz „-? [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();

Służy do wyodrębniania liczb zachowujących ułamek dziesiętny

Kannan
źródło
Nie obsługuje negatywów
OneCricketeer
5

Zaakceptowana odpowiedź wykrywa cyfry, ale nie wykrywa liczb sformatowanych, np. 2000, ani miejsc po przecinku, np. 4.8. Do takiego zastosowania -?\\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);

Wynik: [4.8, 2,000]

Mugoma J. Okomba
źródło
1
@JulienS .: Nie zgadzam się. To wyrażenie regularne robi znacznie więcej, niż żądał OP, i robi to niepoprawnie. (Przynajmniej, część dziesiętna powinna być w opcjonalnym grupy, wszystko w nim wymagane i chciwy: (?:\.\d+)?.)
Alan Moore
Na pewno masz tam punkt na część dziesiętną. Jednak bardzo często można napotkać sformatowane liczby.
Julien
@AlanMoore wielu odwiedzających SO szuka jakichkolwiek / różnych sposobów rozwiązania problemów z różnymi podobieństwami / różnicami, dlatego warto wspomnieć o sugestii. Nawet PO mógł być nadmiernie uproszczony.
Mugoma J. Okomba
4

dla liczb wymiernych użyj tego: (([0-9]+.[0-9]*)|([0-9]*.[0-9]+)|([0-9]+))

Andrey
źródło
1
OP podał liczby całkowite, a nie liczby rzeczywiste. Zapomniałeś też uciec od kropek i żaden z tych nawiasów nie jest potrzebny.
Alan Moore
3

Używając Java 8, możesz:

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]

Jeśli nie masz liczb ujemnych, możesz pozbyć się replaceAll(i użyć !s.isEmpty()w filter), ponieważ służy to tylko do prawidłowego podziału czegoś podobnego 2-34(można to również załatwić wyłącznie za pomocą wyrażenia regularnego w split, ale jest to dość skomplikowane).

Arrays.streamzamienia nasz String[]w Stream<String>.

filterusuwa początkowe i końcowe puste ciągi, a także te, -które nie są częścią liczby.

mapToInt(Integer::parseInt).toArray()apeluje parseIntdo każdego Stringo przyznanie nam pliku int[].


Alternatywnie Java 9 ma metodę Matcher.results , która powinna pozwolić na coś takiego:

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]

W obecnym stanie żaden z nich nie jest dużym postępem w stosunku do zwykłego zapętlania wyników z Pattern/, Matcherjak pokazano w innych odpowiedziach, ale powinno być prostsze, jeśli chcesz kontynuować z bardziej złożonymi operacjami, które są znacznie uproszczone przy użyciu strumienie.

Bernhard Barker
źródło
1

Wyodrębnij wszystkie liczby rzeczywiste za pomocą tego.

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;
    }
}
Łapacz 68
źródło
1

Znaki ułamkowe i grupujące reprezentujące liczby rzeczywiste mogą się różnić w zależności od języka. Ta sama liczba rzeczywista może być zapisana na bardzo różne sposoby, w zależności od języka.

Liczba dwa miliony w języku niemieckim

2 000 000,00

i po angielsku

2.000.000,00

Metoda pełnego wyodrębnienia liczb rzeczywistych z danego ciągu w sposób niezależny od języka:

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;
}
AnDus
źródło
1

Jeśli chcesz wykluczyć liczby zawarte w słowach, takie jak bar1 lub aa1bb, dodaj granice słów \ b do dowolnej odpowiedzi opartej na wyrażeniu regularnym. Na przykład:

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

wyświetlacze:

2
12
dxl
źródło
1

Sugerowałbym sprawdzenie wartości ASCII w celu wyodrębnienia liczb z łańcucha Przypuśćmy, że masz ciąg wejściowy jako moje imię12345 i jeśli chcesz po prostu wyodrębnić liczby 12345, możesz to zrobić najpierw konwertując ciąg na tablicę znaków, a następnie użyj następującego pseudokodu

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

po wyodrębnieniu liczb dołącz je do tablicy

Mam nadzieję że to pomoże

The_Fresher
źródło
Ciąg Java jest zliczaną sekwencją jednostek kodu Unicode / UTF-16. Zgodnie z projektem UTF-16 pierwsze 128 znaków ma taką samą wartość (ale nie taki sam rozmiar), co ich kodowanie ASCII; Poza tym myślenie, że masz do czynienia z ASCII, prowadzi do błędów.
Tom Blodget
0

To wyrażenie było dla mnie najprostsze

String[] extractednums = msg.split("\\\\D++");
user2902302
źródło
-1
public static String extractNumberFromString(String number) {
    String num = number.replaceAll("[^0-9]+", " ");
    return num.replaceAll(" ", "");
}

wyodrębnia tylko liczby z łańcucha

user3509903
źródło