Ciąg Java - Sprawdź, czy ciąg zawiera tylko cyfry, a nie litery

196

Mam ciąg, który ładuję w całej aplikacji i zmienia się on z cyfr na litery i tak dalej. Mam proste ifstwierdzenie, czy zawiera litery lub cyfry, ale coś nie działa poprawnie. Oto fragment kodu.

String text = "abc"; 
String number; 

if (text.contains("[a-zA-Z]+") == false && text.length() > 2) {
    number = text; 
}

Chociaż textzmienna zawiera litery, warunek zwraca wartość jako true. I &&powinny ewaluować, ponieważ oba warunki muszą być spełnione true, aby przetworzyćnumber = text;

==============================

Rozwiązanie:

Udało mi się to rozwiązać za pomocą następującego kodu podanego w komentarzu do tego pytania. Wszystkie pozostałe posty są również ważne!

To, co wykorzystałem, działało, pochodzi z pierwszego komentarza. Chociaż wszystkie podane przykładowe kody również wydają się być prawidłowe!

String text = "abc"; 
String number; 

if (Pattern.matches("[a-zA-Z]+", text) == false && text.length() > 2) {
    number = text; 
}
RedHatcc
źródło
5
zawiera nie przyjmuje wyrażeń regularnych jako danych wejściowych. Użyj albo matches("\\d{2,}")spróbuj albo z - PatterniMatcher
Guillaume Polet
Czy ciąg może mieć wartość dziesiętną lub tylko wartości całkowite?
pseudoramble
3
Dlaczego sprawdzasz text.length ()> 2? Jaki jest powód?
Code Enthusiastic
1
@ RedHatcc Pattern.matches("[a-zA-Z]+", text) == falsemożna uprościć do!Pattern.matches("[a-zA-Z]+", text)
SARose
2
Korzystanie z interfejsu API przesyłania strumieniowego Java z boolean isNumeric = someString.chars().allMatch(x -> Character.isDigit(x));formularza Max MalyshPost.
Yash

Odpowiedzi:

354

Jeśli będziesz przetwarzał numer jako tekst, zmień:

if (text.contains("[a-zA-Z]+") == false && text.length() > 2){

do:

if (text.matches("[0-9]+") && text.length() > 2) {

Zamiast sprawdzać, czy ciąg nie zawiera znaków alfabetycznych, sprawdź, czy zawiera on tylko cyfry.

Jeśli faktycznie chcesz użyć wartości liczbowej, użyj Integer.parseInt()lub, Double.parseDouble()jak wyjaśniono poniżej.


Na marginesie, ogólnie uważa się za złą praktykę porównywanie wartości logicznych z truelub false. Po prostu użyj if (condition)lub if (!condition).

Adam Liss
źródło
25
Prawdopodobnie chcesz dodać kotwice (np. ^[0-9]+$), W przeciwnym razie abc123defbędzie to traktowane jako liczba.
ICR
10
Nie sądzę, że to jest wymagane. matches()zwraca wartość true tylko wtedy, gdy jest to pełne dopasowanie od początku do końca.
Chthonic Project,
4
„^ -? \ d + \.? \ d * $” porówna cały ciąg znaków i będzie pasował tylko wtedy, gdy będzie to poprawna liczba (łącznie z ujemnymi i dziesiętnymi). Na przykład będzie pasować do 1, 10, 1,0, -1, -1,0 itd. Będzie również pasował do „1”. ale i tak często można to przeanalizować.
16
Nie ma potrzeby dzwonić && (text.length() > 2). Wszystko można sprawdzić według wzoru regularnego:if (text.matches("[0-9]{3,}")
ctomek,
Co z przecinkami lub kropkami dla liczb, które nie są liczbami całkowitymi?
nibbana
20

Możesz także użyć NumberUtil.isCreatable (String str) z Apache Commons

Dhrumil Shah
źródło
4
Nie sądzę, NumberUtil.isCreatable(String str)aby można było użyć tego, o co prosi pierwotne pytanie. Np. NumberUtil.isCreatable( "09" )Zwraca false, chociaż "09" zawiera tylko liczby .
Abdull
14

Tak bym to zrobił:

if(text.matches("^[0-9]*$") && text.length() > 2){
    //...
}

$Pozwala uniknąć częściowego dopasowania np; 1B.

tokhi
źródło
1
Nie potrzebuję tej text.length() > 2części, więc po prostu zastąpiłem ^[0-9]*$ją, ^[0-9]+$aby mieć pewność, że mam co najmniej jeden numer.
YB Przyczyna
8

Pod względem wydajności parseInti takie są znacznie gorsze niż inne rozwiązania, ponieważ przynajmniej wymagają obsługi wyjątków.

Przeprowadziłem testy jmh i odkryłem, że iteracja ciągów przy użyciu charAti porównywanie znaków z znakami granicznymi jest najszybszym sposobem sprawdzenia, czy łańcuch zawiera tylko cyfry.

Testy JMH

Testy porównują wydajność Character.isDigitvs Pattern.matcher().matchesvs Long.parseLongvs sprawdzanie wartości char.

Te sposoby mogą dawać różne wyniki dla łańcuchów niepasujących i łańcuchów zawierających znaki +/-.

Testy działają w trybie przepustowości (im większa, tym lepiej ) z 5 iteracjami rozgrzewania i 5 iteracjami testów.

Wyniki

Należy pamiętać, że parseLongjest prawie 100 razy wolniej niż isDigitna pierwszym obciążeniu testowym.

## Test load with 25% valid strings (75% strings contain non-digit symbols)

Benchmark       Mode  Cnt  Score   Error  Units
testIsDigit    thrpt    5  9.275 ± 2.348  ops/s
testPattern    thrpt    5  2.135 ± 0.697  ops/s
testParseLong  thrpt    5  0.166 ± 0.021  ops/s

## Test load with 50% valid strings (50% strings contain non-digit symbols)

Benchmark              Mode  Cnt  Score   Error  Units
testCharBetween       thrpt    5  16.773 ± 0.401  ops/s
testCharAtIsDigit     thrpt    5  8.917 ± 0.767  ops/s
testCharArrayIsDigit  thrpt    5  6.553 ± 0.425  ops/s
testPattern           thrpt    5  1.287 ± 0.057  ops/s
testIntStreamCodes    thrpt    5  0.966 ± 0.051  ops/s
testParseLong         thrpt    5  0.174 ± 0.013  ops/s
testParseInt          thrpt    5  0.078 ± 0.001  ops/s

Zestaw testowy

@State(Scope.Benchmark)
public class StringIsNumberBenchmark {
    private static final long CYCLES = 1_000_000L;
    private static final String[] STRINGS = {"12345678901","98765432177","58745896328","35741596328", "123456789a1", "1a345678901", "1234567890 "};
    private static final Pattern PATTERN = Pattern.compile("\\d+");

    @Benchmark
    public void testPattern() {
        for (int i = 0; i < CYCLES; i++) {
            for (String s : STRINGS) {
                boolean b = false;
                b = PATTERN.matcher(s).matches();
            }
        }
    }

    @Benchmark
    public void testParseLong() {
        for (int i = 0; i < CYCLES; i++) {
            for (String s : STRINGS) {
                boolean b = false;
                try {
                    Long.parseLong(s);
                    b = true;
                } catch (NumberFormatException e) {
                    // no-op
                }
            }
        }
    }

    @Benchmark
    public void testCharArrayIsDigit() {
        for (int i = 0; i < CYCLES; i++) {
            for (String s : STRINGS) {
                boolean b = false;
                for (char c : s.toCharArray()) {
                    b = Character.isDigit(c);
                    if (!b) {
                        break;
                    }
                }
            }
        }
    }

    @Benchmark
    public void testCharAtIsDigit() {
        for (int i = 0; i < CYCLES; i++) {
            for (String s : STRINGS) {
                boolean b = false;
                for (int j = 0; j < s.length(); j++) {
                    b = Character.isDigit(s.charAt(j));
                    if (!b) {
                        break;
                    }
                }
            }
        }
    }

    @Benchmark
    public void testIntStreamCodes() {
        for (int i = 0; i < CYCLES; i++) {
            for (String s : STRINGS) {
                boolean b = false;
                b = s.chars().allMatch(c -> c > 47 && c < 58);
            }
        }
    }

    @Benchmark
    public void testCharBetween() {
        for (int i = 0; i < CYCLES; i++) {
            for (String s : STRINGS) {
                boolean b = false;
                for (int j = 0; j < s.length(); j++) {
                    char charr = s.charAt(j);
                    b = '0' <= charr && charr <= '9';
                    if (!b) {
                        break;
                    }
                }
            }
        }
    }
}

Zaktualizowano 23 lutego 2018 r

  • Dodaj jeszcze dwa przypadki - jeden używa charAtzamiast tworzyć dodatkową tablicę, a drugi używa IntStreamkodów char
  • Dodaj natychmiastową przerwę, jeśli nie znaleziono cyfr dla zapętlonych przypadków testowych
  • Zwraca false dla pustego łańcucha dla zapętlonych przypadków testowych

Zaktualizowano 23 lutego 2018 r

  • Dodaj jeszcze jeden przypadek testowy (najszybszy!), Który porównuje wartość char bez użycia strumienia
Anton R.
źródło
1
Jeśli spojrzysz na kod toCharArray, alokuje on tablicę znaków i kopiuje znaki (myślę, że może to być drogie). A gdyby tak po prostu wykonać iterację za pomocą indeksu i charAt, czy byłoby to szybsze? Byłoby również interesujące, gdybyś mógł dodać rozwiązanie od Andy'ego do swoich testów: boolean isNum = text.chars (). AllMatch (c -> c> = 48 && c <= 57)
Aldo Canepa
8

Aby po prostu sprawdzić ciąg, który zawiera tylko ALFABETY, użyj następującego kodu:

if (text.matches("[a-zA-Z]+"){
   // your operations
}

Aby po prostu sprawdzić ciąg, który zawiera tylko NUMBER, użyj następującego kodu:

if (text.matches("[0-9]+"){
   // your operations
}

Mam nadzieję, że to komuś pomoże!

Aman Kumar Gupta
źródło
3

boolean isNum = text.chars (). allMatch (c -> c> = 48 && c <= 57)

Andy
źródło
1
aby zmniejszyć liczby magiczne, możesz porównać w następujący sposób:boolean isNum = text.chars().allMatch(c -> c >= '0' && c <= '9')
Phe0nix
2

Możesz użyć Regex.Match

if(text.matches("\\d*")&& text.length() > 2){
    System.out.println("number");
}

Lub możesz użyć wersji podobnych Integer.parseInt(String)lub lepszych Long.parseLong(String)dla większych liczb, takich jak na przykład:

private boolean onlyContainsNumbers(String text) {
    try {
        Long.parseLong(text);
        return true;
    } catch (NumberFormatException ex) {
        return false;
    }
} 

A następnie przetestuj za pomocą:

if (onlyContainsNumbers(text) && text.length() > 2) {
    // do Stuff
}
Yannjoel
źródło
.matches ("^ \\ d + $")
CrandellWS
2

Poniżej wyrażeń regularnych można sprawdzić, czy ciąg ma tylko liczbę, czy nie:

if (str.matches(".*[^0-9].*")) or if (str.matches(".*\\D.*"))

Oba powyższe warunki zostaną zwrócone, truejeśli ciąg znaków zawiera cyfry inne niż liczby. Na falseciąg ma tylko liczby.

Nitesh Shrivastava
źródło
2

Apache Commons Lang zapewnia org.apache.commons.lang.StringUtils.isNumeric(CharSequence cs), który przyjmuje jako argument a Stringi sprawdza, czy składa się ze znaków czysto numerycznych (w tym liczb ze skryptów niełacińskich). Ta metoda zwraca, falsejeśli występują takie znaki, jak spacja, minus, plus oraz separatory dziesiętne, takie jak przecinek i kropka.

Inne metody tej klasy pozwalają na dalsze kontrole numeryczne.

Abdull
źródło
1
Powinno to być znacznie szybsze niż wyrażenie regularne; oto implementacja: public static boolean isNumeric(String str) { if (str == null) { return false; } else { int sz = str.length(); for(int i = 0; i < sz; ++i) { if (!Character.isDigit(str.charAt(i))) { return false; } } return true; } }
Leo
1

Istnieje wiele możliwości uzyskiwania liczb ze Strings w Javie (i odwrotnie). Możesz pominąć część wyrażenia regularnego, aby uniknąć komplikacji.

Na przykład możesz spróbować zobaczyć, co Double.parseDouble(String s)za ciebie zwraca. Powinien wyrzucić a, NumberFormatExceptionjeśli nie znajdzie odpowiedniej wartości w ciągu. Sugerowałbym tę technikę, ponieważ faktycznie można skorzystać z wartości reprezentowanej przez Stringjako typ liczbowy.

pseudoramble
źródło
5
Używanie wyjątku jako powodu do testowania danych wejściowych może być złym pomysłem, wyjątki powodują duży narzut.
Ofir Luzon,
1
@OfirLuzon Zgadzam się, że wyjątki nie są świetnym sposobem radzenia sobie z oczekiwanymi przypadkami, które się pojawią. Jednak myślę, że trudno powiedzieć, czy byłby to hit wydajności bez większego kontekstu.
pseudoramble,
1

Oto mój kod, mam nadzieję, że ci to pomoże!

 public boolean isDigitOnly(String text){

    boolean isDigit = false;

    if (text.matches("[0-9]+") && text.length() > 2) {
        isDigit = true;
    }else {
        isDigit = false;
    }

    return isDigit;
}
Saurabh Gaddelpalliwar
źródło
0

Ten kod jest już napisany. Jeśli nie przeszkadza ci (wyjątkowo) niewielkie uderzenie wydajności - które prawdopodobnie nie jest gorsze niż dopasowanie wyrażenia regularnego - użyj Integer.parseInt () lub Double.parseDouble () . Dzięki temu od razu dowiesz się, czy String to tylko liczby (lub odpowiednio liczba). Jeśli potrzebujesz obsługiwać dłuższe ciągi liczb, zarówno konstruktory sportowe BigInteger , jak i BigDecimal, które akceptują ciągi. Każde z nich zgłosi wyjątek NumberFormatException, jeśli spróbujesz przekazać go bez liczby (całkowite lub dziesiętne, na podstawie oczywiście wybranego). Alternatywnie, w zależności od wymagań, po prostu iteruj znaki w ciągu znaków i zaznacz Character.isDigit ()i / lub Character.isLetter () .

Ryan Stewart
źródło
0
import java.util.*;

class Class1 {
    public static void main(String[] argh) {
        boolean ans = CheckNumbers("123");
        if (ans == true) {
            System.out.println("String contains numbers only");
        } else {
            System.out.println("String contains other values as well");

        }
    }


    public static boolean CheckNumbers(String input) {
        for (int ctr = 0; ctr < input.length(); ctr++) {
            if ("1234567890".contains(Character.valueOf(input.charAt(ctr)).toString())) {
                continue;
            } else {
                return false;
            }
        }
        return true;
    }
}
Usman Javaid
źródło
0
Character first_letter_or_number = query.charAt(0);
                //------------------------------------------------------------------------------
                if (Character.isDigit())
                {

                }
                else if (Character.isLetter())
                {

                }
JamisonMan111
źródło
0

Przykład testu roboczego

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

public class PaserNo {

    public static void main(String args[]) {

        String text = "gg";

        if (!StringUtils.isBlank(text)) {
            if (stringContainsNumber(text)) {
                int no=Integer.parseInt(text.trim());
                System.out.println("inside"+no);

            } else {
                System.out.println("Outside");
            }
        }
        System.out.println("Done");
    }

    public static boolean stringContainsNumber(String s) {
        Pattern p = Pattern.compile("[0-9]");
        Matcher m = p.matcher(s);
        return m.find();
    }
}

Nadal twój kod może być podzielony na „1a” itp., Więc musisz sprawdzić wyjątek

if (!StringUtils.isBlank(studentNbr)) {
                try{
                    if (isStringContainsNumber(studentNbr)){
                    _account.setStudentNbr(Integer.parseInt(studentNbr.trim()));
                }
                }catch(Exception e){
                    e.printStackTrace();
                    logger.info("Exception during parse studentNbr"+e.getMessage());
                }
            }

Metodą sprawdzania nie jest ciąg znaków lub nie

private boolean isStringContainsNumber(String s) {
        Pattern p = Pattern.compile("[0-9]");
        Matcher m = p.matcher(s);
        return m.find();
    }
Vaquar Khan
źródło
0

Złą praktyką jest angażowanie wyjątków rzucających / obsługujących w taki typowy scenariusz.

Dlatego parseInt () nie jest ładny, ale regex to eleganckie rozwiązanie tego problemu, ale dbać o następujących:
-fractions
numery -wykluczające
-decimal separator może się różnić w contries ( „”, np „” lub)
-sometimes dozwolone jest stosowanie tak zwanego separatora tysięcy, takiego jak spacja lub przecinek, np. 12 324 1000 3555

Aby obsłużyć wszystkie niezbędne przypadki w aplikacji, musisz zachować ostrożność, ale ten regex obejmuje typowe scenariusze (dodatnie / ujemne i ułamkowe, oddzielone kropką): ^ [- +]? \ D *.? \ D + $
For testowanie, polecam regexr.com .

Adam Bodrogi
źródło
0

Nieznacznie zmodyfikowana wersja Adama Bodrogiego:

public class NumericStr {


public static void main(String[] args) {
    System.out.println("Matches: "+NumericStr.isNumeric("20"));         // Should be true
    System.out.println("Matches: "+NumericStr.isNumeric("20,00"));          // Should be true
    System.out.println("Matches: "+NumericStr.isNumeric("30.01"));          // Should be true
    System.out.println("Matches: "+NumericStr.isNumeric("30,000.01"));          // Should be true
    System.out.println("Matches: "+NumericStr.isNumeric("-2980"));          // Should be true
    System.out.println("Matches: "+NumericStr.isNumeric("$20"));            // Should be true
    System.out.println("Matches: "+NumericStr.isNumeric("jdl"));            // Should be false
    System.out.println("Matches: "+NumericStr.isNumeric("2lk0"));           // Should be false
}

public static boolean isNumeric(String stringVal) {
    if (stringVal.matches("^[\\$]?[-+]?[\\d\\.,]*[\\.,]?\\d+$")) {
        return true;
    }

    return false;
}
}

Musiałem użyć tego dzisiaj, więc właśnie opublikowałem moje modyfikacje. Obejmuje notację walutową, tysiące przecinków lub okresów oraz niektóre walidacje. Nie obejmuje innych notowań walutowych (euro, cent), przecinki weryfikacyjne są co trzecią cyfrą.

użytkownik176692
źródło