Jak sprawdzić, czy ciąg znaków jest liczbowy w Javie


886

Jak sprawdziłbyś, czy Łańcuch był liczbą przed analizą?


36
Wszystkie rozwiązania proponowane z wyrażeniami regularnymi nie będą działać dla liczb szesnastkowych.
Oscar Castiblanco

a przekazanie łańcucha zerowego w funkcji dopasowania (...) spowoduje zgłoszenie wyjątku NullPointer.
Hitesh Sahu

Zobacz odpowiedź Maxa Malysha na zwięzłe rozwiązanie Java 8 bez bibliotek innych firm.
Andy Thomas

@HiteshSahu ciągi zerowe wydają się być obsługiwane z wdziękiem w najnowszej wersji (w tym Java 6.xi 7.x)
lifebalance

Wszystkie rozwiązania proponowane do użycia Integer.parseInt()nie będą w stanie przeanalizować numerów telefonów komórkowych NumberFormatException.
Nie błąd

Odpowiedzi:


691

Z Apache Commons Lang 3.5 i nowszym: NumberUtils.isCreatablelubStringUtils.isNumeric .

Z Apache Commons Lang 3.4 i niższymi: NumberUtils.isNumberlub StringUtils.isNumeric.

Możesz także użyć, StringUtils.isNumericSpacektóre zwraca truepuste ciągi i ignoruje wewnętrzne spacje w ciągu. Innym sposobem jest użycie, NumberUtils.isParsablektóre zasadniczo sprawdza, czy liczba jest możliwa do parsowania zgodnie z Javą. (Połączone javadocs zawierają szczegółowe przykłady dla każdej metody.)


59
StringUtils.isNumeric()prawdopodobnie nie byłoby to właściwe tutaj, ponieważ sprawdza tylko, czy ciąg znaków jest ciągiem cyfr. Byłoby dobrze dla większości liczb całkowitych, ale nie dla liczb z ułamkami dziesiętnymi, separatorów grup itp.
Jeff Mercado

42
wymyśl koło na nowo, ponieważ nie dołączasz całej biblioteki, ponieważ potrzebujesz funkcji 3-liniowej w jednym miejscu.
dalvarezmartinez1

12
Czy naprawdę warto jednak dodać całą bibliotekę dla tej funkcji? Oczywiście, jeśli jest używany z innymi rzeczami, które są świetne, ale prawdopodobnie jest to przesada, biorąc pod uwagę, że ludzie rozwiązali to w jednym wierszu kodu.
Woda

7
Nie działa z negatywami. A połowa wszystkich liczb jest ujemna, więc .....
Paul Draper

6
@PaulDraper: Masz rację, StringUtilsnie obsługuje znaków wiodących, ale powinieneś sprawdzić NumberUtils.isCreatable, poprawnie obsługuje negatywy.
palacsint,

904

Zasadniczo odbywa się to za pomocą prostej funkcji zdefiniowanej przez użytkownika (tj. „Włóż własną funkcję„ isNumeric ”).

Coś jak:

public static boolean isNumeric(String str) { 
  try {  
    Double.parseDouble(str);  
    return true;
  } catch(NumberFormatException e){  
    return false;  
  }  
}

Jeśli jednak często wywołujesz tę funkcję i oczekujesz, że wiele kontroli zakończy się niepowodzeniem z powodu braku bycia liczbą, to działanie tego mechanizmu nie będzie świetne, ponieważ polegasz na wyjątkach zgłaszanych dla każdej awarii, co jest dość kosztowną operacją.

Alternatywnym podejściem może być użycie wyrażenia regularnego w celu sprawdzenia ważności bycia liczbą:

public static boolean isNumeric(String str) {
  return str.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.
}

Uważaj jednak na powyższy mechanizm RegEx, ponieważ zawiedzie, jeśli używasz cyfr spoza arabskiego (tj. Cyfr od 0 do 9). Wynika to z faktu, że część „\ d” RegEx będzie pasować tylko do [0-9] i faktycznie nie jest międzynarodowo świadoma liczbowo. (Podziękowania dla OregonGhost za zwrócenie na to uwagi!)

Lub jeszcze inną alternatywą jest użycie wbudowanego obiektu Java java.text.NumberFormat, aby sprawdzić, czy po analizie ciągu pozycja parsera znajduje się na końcu ciągu. Jeśli tak, możemy założyć, że cały ciąg jest liczbowy:

public static boolean isNumeric(String str) {
  NumberFormat formatter = NumberFormat.getInstance();
  ParsePosition pos = new ParsePosition(0);
  formatter.parse(str, pos);
  return str.length() == pos.getIndex();
}

7
Czy \ d w Java Regex pasuje tylko do cyfr łacińskich? Jeśli jest to wyrażenie regularne
OregonGhost

3
rozwiązanie numberFormatter jest prawdopodobnie tylko nieznacznie lepsze niż wyłapanie wyjątku NumberFormatException jeden. Podejrzewam, że najlepszym sposobem jest użycie wyrażenia regularnego.
Chii,

11
Zauważ, że .w wyrażeniu regularnym będzie pasować dowolny znak, a nie tylko separator dziesiętny.
jqno

9
+1 za realizację kosztu wypróbowania / złapania. To naprawdę okropne podejście do używania na dłuższą metę do wielokrotnego użytku, ale tak naprawdę utknęliśmy z tym w Javie.
demongolem

5
Zauważ, że nie ma czegoś takiego jak „cyfry łacińskie”, a cyfry 0–9 są w rzeczywistości cyframi arabskimi. Ludzie prawdopodobnie znają cyfry rzymskie, których używali ludzie, którzy mówili po łacinie, w postaci I, II, III, IV, V, VI itp. En.wikipedia.org/wiki/Arabic_numerals ; en.wikipedia.org/wiki/Roman_numerals
dantiston

152

jeśli używasz Androida, powinieneś użyć:

android.text.TextUtils.isDigitsOnly(CharSequence str)

dokumentacja znajduje się tutaj

zachowaj prostotę . przeważnie każdy może „przeprogramować” (to samo).


4
@ kape123 :) na pewno „123.456” nie zawiera cyfr.
Ahmed Alejo

8
Uwaga: powoduje to NPE dla wejścia zerowego. Ponadto nie działa z liczbami ujemnymi lub dziesiętnymi.
gMale,

2
Lubię to!! Myślę, że to jest absolutnie dla cyfr. Nie .,-
iluzja

Właśnie tego szukałem. Coś prostego do sprawdzenia tylko dla cyfr 0–9. Ustawiłem filtr w deklaracji mojego EditText, ale na wypadek, gdyby zmiany zostały zmienione lub zastąpione w drodze, miło jest mieć również prostą kontrolę programową.
jwehrle

127

Wyrażenia lambda Java 8.

String someString = "123123";
boolean isNumeric = someString.chars().allMatch( Character::isDigit );

4
Możesz także użyć odwołania do metody: someString.chars (). AllMatch (Character :: isDigit)
Wienczny

3
Fajnie, ale wciąż wymyśla koło na nowo, tak jak prawie wszystkie „rozwiązania” tutaj. Również kończy się niepowodzeniem na „null” (jak prawie wszystkie inne).
qben

8
Ta odpowiedź jest zwięzła, prosta i czytelna. Można go prawie odczytać jak po angielsku - „znaki wszystkich pasujących cyfr”. Nie wymaga bibliotek stron trzecich. Nie stosuje wyjątków w wyjątkowych przypadkach. To powinno stać się przyjętą odpowiedzią.
Andy Thomas

14
Co będzie produkować dla „-1”?
Balázs Németh

2
Niewłaściwa odpowiedź. Ciąg liczbowy może zawierać znaki nienumeryczne (np. „.” Lub „-”) i nadal być doskonale numeryczny. Na przykład 0,5, -1 i 1000 nie powiedzie się z tą odpowiedzią, a mimo to są doskonale numeryczne.
Simeon G

125

Jak wspomniał @CraigTP w swojej doskonałej odpowiedzi, mam podobne obawy dotyczące wydajności przy użyciu wyjątków w celu sprawdzenia, czy łańcuch jest liczbowy, czy nie. W rezultacie dzielę ciąg i używam java.lang.Character.isDigit().

public static boolean isNumeric(String str)
{
    for (char c : str.toCharArray())
    {
        if (!Character.isDigit(c)) return false;
    }
    return true;
}

Według Javadoc , Character.isDigit(char)poprawnie rozpoznaje niełacińskie cyfr. Pod względem wydajności myślę, że prosta liczba N porównań, w których N jest liczbą znaków w łańcuchu, byłaby bardziej wydajna obliczeniowo niż dopasowanie wyrażenia regularnego.

AKTUALIZACJA: Jak zauważył Jean-François Corbett w komentarzu, powyższy kod sprawdziłby tylko liczby całkowite dodatnie, co obejmuje większość mojego przypadku użycia. Poniżej znajduje się zaktualizowany kod, który poprawnie sprawdza poprawność liczb dziesiętnych zgodnie z domyślnymi ustawieniami narodowymi używanymi w systemie, przy założeniu, że separator dziesiętny występuje tylko raz w ciągu.

public static boolean isStringNumeric( String str )
{
    DecimalFormatSymbols currentLocaleSymbols = DecimalFormatSymbols.getInstance();
    char localeMinusSign = currentLocaleSymbols.getMinusSign();

    if ( !Character.isDigit( str.charAt( 0 ) ) && str.charAt( 0 ) != localeMinusSign ) return false;

    boolean isDecimalSeparatorFound = false;
    char localeDecimalSeparator = currentLocaleSymbols.getDecimalSeparator();

    for ( char c : str.substring( 1 ).toCharArray() )
    {
        if ( !Character.isDigit( c ) )
        {
            if ( c == localeDecimalSeparator && !isDecimalSeparatorFound )
            {
                isDecimalSeparatorFound = true;
                continue;
            }
            return false;
        }
    }
    return true;
}

4
Czy separator dziesiętny również nie spowoduje tego?
Jean-François Corbett

1
@ Jean-FrançoisCorbett: Dobrze, zaktualizowałem kod do nowszego, który akceptuje separatory dziesiętne.
Ibrahim Arief

2
Czy znak -ve nie spełnia tej funkcji?
java_mouse

3
Wywołanie toCharArray()spowoduje utworzenie kopii tablicy w obiekcie String, ponieważ ciągi są niezmienne. Prawdopodobnie szybciej użyć charAt(int index)metody bezpośrednio na obiekcie String.
Mike Kucera,

2
Wygeneruje StringIndexOutOfBoundsExceptionkiedy przeszedł ciąg o długości 0. Może być zamocowaneif(str.length() == 0) return false;
samgak

43

Biblioteka Guava Google zapewnia miły metody pomocnika, aby to zrobić: Ints.tryParse. Używasz go w ten sposób, Integer.parseIntale zwraca on nullzamiast rzucać wyjątek, jeśli ciąg nie parsuje prawidłowej liczby całkowitej. Zauważ, że zwraca Integer, a nie int, więc musisz przekonwertować / autobox z powrotem na int.

Przykład:

String s1 = "22";
String s2 = "22.2";
Integer oInt1 = Ints.tryParse(s1);
Integer oInt2 = Ints.tryParse(s2);

int i1 = -1;
if (oInt1 != null) {
    i1 = oInt1.intValue();
}
int i2 = -1;
if (oInt2 != null) {
    i2 = oInt2.intValue();
}

System.out.println(i1);  // prints 22
System.out.println(i2);  // prints -1

Jednak od obecnego wydania - Guava r11 - nadal jest oznaczony @Beta.

Nie porównałem tego. Patrząc na kod źródłowy, pojawia się pewien narzut związany z dużą ilością sprawdzania poprawności, ale w końcu używają Character.digit(string.charAt(idx))podobnej, ale nieco innej niż odpowiedź z @Ibrahim powyżej. W ich implementacji nie ma wyjątku obsługa kosztów ogólnych pod przykryciem.


Uważaj, że spowoduje to wyrzucenie NPE, jeśli argument jest pusty.
Vadzim

30

Nie używaj wyjątków do sprawdzania poprawności swoich wartości. Zamiast tego użyj Util libs jak apache NumberUtils:

NumberUtils.isNumber(myStringValue);

Edytuj :

Zauważ, że jeśli łańcuch zaczyna się od 0, NumberUtils zinterpretuje twoją wartość jako szesnastkową.

NumberUtils.isNumber("07") //true
NumberUtils.isNumber("08") //false

7
Przyjęta odpowiedź sprzed trzech lat już obejmuje Number.isNumber().
Andy Thomas

Nie wydaje mi się Został zaktualizowany lub zmienił zaakceptowaną odpowiedź. Pamiętam, że zaakceptowana odpowiedź nie obejmowała NumberUtils, dlatego dodałem swoją odpowiedź. Ale dziękuję za komentarz
Goot

2
@Goot - Historia zaakceptowanej odpowiedzi pokazuje, że Number.isNumber()była obecna od pierwszej wersji odpowiedzi, datowanej na 24 września 12 o 17:01.
Andy Thomas

@Goot, jest to całkiem dobre, ponieważ obejmuje również sprawdzanie wartości dziesiętnej, w przeciwieństwie do StringUtils.
Heena Hussain

24

Dlaczego wszyscy naciskają na rozwiązania wyjątkowe / wyrażenia regularne?

Chociaż rozumiem, że większość ludzi nie ma nic przeciwko używaniu try / catch, jeśli chcesz to robić często ... może to być bardzo obciążające.

To, co zrobiłem tutaj, to wzięcie wyrażenia regularnego, metod parseNumber () i metody przeszukiwania tablicy, aby sprawdzić, która z nich była najbardziej wydajna. Tym razem spojrzałem tylko na liczby całkowite.

public static boolean isNumericRegex(String str) {
    if (str == null)
        return false;
    return str.matches("-?\\d+");
}

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    char[] data = str.toCharArray();
    if (data.length <= 0)
        return false;
    int index = 0;
    if (data[0] == '-' && data.length > 1)
        index = 1;
    for (; index < data.length; index++) {
        if (data[index] < '0' || data[index] > '9') // Character.isDigit() can go here too.
            return false;
    }
    return true;
}

public static boolean isNumericException(String str) {
    if (str == null)
        return false;
    try {  
        /* int i = */ Integer.parseInt(str);
    } catch (NumberFormatException nfe) {  
        return false;  
    }
    return true;
}

Wyniki, które uzyskałem, były następujące:

Done with: for (int i = 0; i < 10000000; i++)...

With only valid numbers ("59815833" and "-59815833"):
    Array numeric took 395.808192 ms [39.5808192 ns each]
    Regex took 2609.262595 ms [260.9262595 ns each]
    Exception numeric took 428.050207 ms [42.8050207 ns each]
    // Negative sign
    Array numeric took 355.788273 ms [35.5788273 ns each]
    Regex took 2746.278466 ms [274.6278466 ns each]
    Exception numeric took 518.989902 ms [51.8989902 ns each]
    // Single value ("1")
    Array numeric took 317.861267 ms [31.7861267 ns each]
    Regex took 2505.313201 ms [250.5313201 ns each]
    Exception numeric took 239.956955 ms [23.9956955 ns each]
    // With Character.isDigit()
    Array numeric took 400.734616 ms [40.0734616 ns each]
    Regex took 2663.052417 ms [266.3052417 ns each]
    Exception numeric took 401.235906 ms [40.1235906 ns each]

With invalid characters ("5981a5833" and "a"):
    Array numeric took 343.205793 ms [34.3205793 ns each]
    Regex took 2608.739933 ms [260.8739933 ns each]
    Exception numeric took 7317.201775 ms [731.7201775 ns each]
    // With a single character ("a")
    Array numeric took 291.695519 ms [29.1695519 ns each]
    Regex took 2287.25378 ms [228.725378 ns each]
    Exception numeric took 7095.969481 ms [709.5969481 ns each]

With null:
    Array numeric took 214.663834 ms [21.4663834 ns each]
    Regex took 201.395992 ms [20.1395992 ns each]
    Exception numeric took 233.049327 ms [23.3049327 ns each]
    Exception numeric took 6603.669427 ms [660.3669427 ns each] if there is no if/null check

Oświadczenie: Nie twierdzę, że te metody są w 100% zoptymalizowane, służą jedynie do demonstracji danych

Wyjątki wygrane wtedy i tylko wtedy, gdy liczba ma 4 znaki lub mniej, a każdy ciąg jest zawsze liczbą ... w takim przypadku, po co w ogóle mieć czek?

Krótko mówiąc, jest to bardzo bolesne, jeśli często próbujesz złapać nieprawidłowe liczby za pomocą try / catch, co ma sens. Ważną zasadą, której zawsze przestrzegam jest NIGDY nie używaj try / catch do przebiegu programu . To jest przykład dlaczego.

Co ciekawe, prosty jeśli char <0 || > 9 było niezwykle łatwe do napisania, łatwe do zapamiętania (i powinno działać w wielu językach) i wygrywa prawie wszystkie scenariusze testowe.

Jedynym minusem jest to, że zgaduję, że Integer.parseInt () może obsługiwać liczby inne niż ASCII, podczas gdy metoda wyszukiwania tablicowego nie.


Dla tych, którzy zastanawiają się, dlaczego powiedziałem, że łatwo jest zapamiętać tablicę znaków, jeśli wiesz, że nie ma żadnych negatywnych znaków, możesz łatwo uciec od czegoś skondensowanego:

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    for (char c : str.toCharArray())
        if (c < '0' || c > '9')
            return false;
    return true;

Wreszcie, jako ostatnia uwaga, byłem ciekawy operatora przypisania w przyjętym przykładzie z wszystkimi głosami. Dodanie w przypisaniu

double d = Double.parseDouble(...)

jest nie tylko bezużyteczny, ponieważ nawet nie używasz tej wartości, ale marnuje czas przetwarzania i wydłuża czas działania o kilka nanosekund (co doprowadziło do wzrostu testów o 100-200 ms). Nie rozumiem, dlaczego ktoś miałby to zrobić, ponieważ w rzeczywistości jest to dodatkowa praca w celu zmniejszenia wydajności.

Można by pomyśleć, że zostałby zoptymalizowany ... chociaż może powinienem sprawdzić kod bajtowy i zobaczyć, co robi kompilator. To nie tłumaczy, dlaczego zawsze wydawało mi się, że jest dłuższe, jeśli jest w jakiś sposób zoptymalizowane ... dlatego zastanawiam się, co się dzieje. Uwaga: przez dłuższy czas mam na myśli uruchomienie testu dla iteracji 10000000, a wielokrotne uruchomienie tego programu (10x +) zawsze pokazało, że jest wolniejszy.

EDYCJA: Zaktualizowano test dla Character.isDigit ()


4
Czy to nie kompiluje za każdym razem nowego wyrażenia regularnego? To nie wydaje się bardzo skuteczne.
Samuel Edwin Ward,

1
@SamuelEdwinWard To cały powód, dla którego napisałem ten post ... przykład wyrażenia regularnego wykorzystał odpowiedzi innych osób i pokazał, jak mało wydajny. Nawet jeśli spróbujesz regex z kompilacją z wyprzedzeniem i tylko przy użyciu tego, różnice czasowe wynoszą: 2587 ms dla wyrażenia regularnego, które opublikowałem od innych udostępnionych osób, 950 ms przy kompilacji z wyprzedzeniem, 144 ms podczas robienia tego jako tablica numeryczna (dla 1 miliona iteracji tego samego łańcucha). Kompilacja z wyprzedzeniem oczywiście pomogłaby, ale niestety nadal jest znacznie gorsza od sposobu tablicowego ... chyba że istnieje jakaś szalona optymalizacja, o której nie wiem.
Woda

Wiara w to, że Regex przyspiesza sprawę, jest niemal błędem. Jeśli jest to jednorazowe wyszukiwanie, tak, rozumiem ... ale zauważyłem, że skutecznie napisany kod faktycznie przewyższa wyrażenia regularne na tyle, aby cię zszokować! Świetny post @Water
Yo Apps,

19
public static boolean isNumeric(String str)
{
    return str.matches("-?\\d+(.\\d+)?");
}

Wyrażenie regularne CraigTP (pokazane powyżej) powoduje pewne fałszywe alarmy. Np. „23y4” będzie liczone jako liczba, ponieważ „.” dopasowuje dowolny znak inny niż przecinek dziesiętny.

Odrzuci także dowolną liczbę z wiodącym „+”

Alternatywą, która pozwala uniknąć tych dwóch drobnych problemów, jest

public static boolean isNumeric(String str)
{
    return str.matches("[+-]?\\d*(\\.\\d+)?");
}

to zwróci truepojedynczy plus "+"lub minus "-", a falsedla"0."
user85421

Dobry połów na pojedynczym plusie lub minusie. Jest „0” prawidłowy numer?
user872985,

"0."jest ważny Double.parseDouble()i jest prawidłowym literałem zgodnie z JLS ( §3.10.2 )!
user85421,

Tworzenie wyrażeń regularnych jest również kosztowne. Wyrażenie regularne należy utworzyć raz i użyć ponownie
Daniel Nuriyev

1
powinieneś to zmienić namatches("-?\\d+([.]\\d+)?")
Bobs

14

Możemy spróbować zastąpić wszystkie liczby z podanego ciągu ciągiem („”), tj. Spacją, a jeśli po tym czasie długość łańcucha wyniesie zero, możemy powiedzieć, że podany ciąg zawiera tylko liczby. [Jeśli uznasz, że ta odpowiedź była pomocna, zastanów się nad jej głosowaniem] Przykład:

boolean isNumber(String str){
        if(str.length() == 0)
            return false; //To check if string is empty

        if(str.charAt(0) == '-')
            str = str.replaceFirst("-","");// for handling -ve numbers

        System.out.println(str);

        str = str.replaceFirst("\\.",""); //to check if it contains more than one decimal points

        if(str.length() == 0)
            return false; // to check if it is empty string after removing -ve sign and decimal point
        System.out.println(str);

        return str.replaceAll("[0-9]","").length() == 0;
    }

Więc ""jest wiele, ale "3.14"i "-1"nie są?
Eric Duminil

Najwyraźniej nie dotyczy wszystkich form liczbowych, ale oto głosowanie za odmiennym myśleniem ... jeśli oryginalna myśl była twoja, to znaczy.
gbenroscience

12

Możesz użyć NumberFormat#parse:

try
{
     NumberFormat.getInstance().parse(value);
}
catch(ParseException e)
{
    // Not a number.
}

Zaproponowano edycję - brakowało .getInstance (). +1, ponieważ była to odpowiedź, na którą poszedłem, znajdując to pytanie.
8bitjunkie 10.04. O

5
Kosztowne, jeśli stosowane w
szerokim zakresie

1
Przejdzie również, jeśli na końcu znajdują się postacie śmieci value.
Brian White

Powoduje to powstanie problemu z sonarem, jeśli nie zarejestrujesz wyjątku
jmhostalet

1
Działa to dla formatu liczb 0x0001, w którym Double.parseDouble nie działało. +1
Seabass77


8

Oto moja odpowiedź na problem.

Łapacz wszystkim metoda wygoda, które można wykorzystać do analizowania dowolny ciąg z dowolnym typem parsera: isParsable(Object parser, String str). Analizator składni może być a Classlub an object. Umożliwi to również użycie napisanych przez Ciebie niestandardowych parserów i powinno działać na zawsze, np .:

isParsable(Integer.class, "11");
isParsable(Double.class, "11.11");
Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");
isParsable(dateFormater, "2001.07.04 AD at 12:08:56 PDT");

Oto mój kod wraz z opisami metod.

import java.lang.reflect.*;

/**
 * METHOD: isParsable<p><p>
 * 
 * This method will look through the methods of the specified <code>from</code> parameter
 * looking for a public method name starting with "parse" which has only one String
 * parameter.<p>
 * 
 * The <code>parser</code> parameter can be a class or an instantiated object, eg:
 * <code>Integer.class</code> or <code>new Integer(1)</code>. If you use a
 * <code>Class</code> type then only static methods are considered.<p>
 * 
 * When looping through potential methods, it first looks at the <code>Class</code> associated
 * with the <code>parser</code> parameter, then looks through the methods of the parent's class
 * followed by subsequent ancestors, using the first method that matches the criteria specified
 * above.<p>
 * 
 * This method will hide any normal parse exceptions, but throws any exceptions due to
 * programmatic errors, eg: NullPointerExceptions, etc. If you specify a <code>parser</code>
 * parameter which has no matching parse methods, a NoSuchMethodException will be thrown
 * embedded within a RuntimeException.<p><p>
 * 
 * Example:<br>
 * <code>isParsable(Boolean.class, "true");<br>
 * isParsable(Integer.class, "11");<br>
 * isParsable(Double.class, "11.11");<br>
 * Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");<br>
 * isParsable(dateFormater, "2001.07.04 AD at 12:08:56 PDT");<br></code>
 * <p>
 * 
 * @param parser    The Class type or instantiated Object to find a parse method in.
 * @param str   The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @throws java.lang.NoSuchMethodException If no such method is accessible 
 */
public static boolean isParsable(Object parser, String str) {
    Class theClass = (parser instanceof Class? (Class)parser: parser.getClass());
    boolean staticOnly = (parser == theClass), foundAtLeastOne = false;
    Method[] methods = theClass.getMethods();

    // Loop over methods
    for (int index = 0; index < methods.length; index++) {
        Method method = methods[index];

        // If method starts with parse, is public and has one String parameter.
        // If the parser parameter was a Class, then also ensure the method is static. 
        if(method.getName().startsWith("parse") &&
            (!staticOnly || Modifier.isStatic(method.getModifiers())) &&
            Modifier.isPublic(method.getModifiers()) &&
            method.getGenericParameterTypes().length == 1 &&
            method.getGenericParameterTypes()[0] == String.class)
        {
            try {
                foundAtLeastOne = true;
                method.invoke(parser, str);
                return true; // Successfully parsed without exception
            } catch (Exception exception) {
                // If invoke problem, try a different method
                /*if(!(exception instanceof IllegalArgumentException) &&
                   !(exception instanceof IllegalAccessException) &&
                   !(exception instanceof InvocationTargetException))
                        continue; // Look for other parse methods*/

                // Parse method refuses to parse, look for another different method
                continue; // Look for other parse methods
            }
        }
    }

    // No more accessible parse method could be found.
    if(foundAtLeastOne) return false;
    else throw new RuntimeException(new NoSuchMethodException());
}


/**
 * METHOD: willParse<p><p>
 * 
 * A convienence method which calls the isParseable method, but does not throw any exceptions
 * which could be thrown through programatic errors.<p>
 * 
 * Use of {@link #isParseable(Object, String) isParseable} is recommended for use so programatic
 * errors can be caught in development, unless the value of the <code>parser</code> parameter is
 * unpredictable, or normal programtic exceptions should be ignored.<p>
 * 
 * See {@link #isParseable(Object, String) isParseable} for full description of method
 * usability.<p>
 * 
 * @param parser    The Class type or instantiated Object to find a parse method in.
 * @param str   The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @see #isParseable(Object, String) for full description of method usability 
 */
public static boolean willParse(Object parser, String str) {
    try {
        return isParsable(parser, str);
    } catch(Throwable exception) {
        return false;
    }
}

5

Aby dopasować tylko dodatnie liczby całkowite dziesięciu, które zawierają tylko cyfry ASCII, użyj:

public static boolean isNumeric(String maybeNumeric) {
    return maybeNumeric != null && maybeNumeric.matches("[0-9]+");
}

5

Skuteczne podejście pozwalające uniknąć prób połowu i obsługi liczb ujemnych i notacji naukowej.

Pattern PATTERN = Pattern.compile( "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E\\d+)?$" );

public static boolean isNumeric( String value ) 
{
    return value != null && PATTERN.matcher( value ).matches();
}

5

Oto moja klasa do sprawdzania, czy łańcuch jest liczbowy. Naprawia również ciągi liczbowe:

Funkcje:

  1. Usuwa zbędne zera [„12.0000000” -> „12”]
  2. Usuwa niepotrzebne zera [„12.0580000” -> „12.058”]
  3. Usuwa znaki nienumeryczne [„12.00sdfsdf00” -> „12”]
  4. Obsługuje ujemne wartości ciągu [„-12,020000” -> „-12.02”]
  5. Usuwa wiele kropek [„-12.0.20.000” -> „-12.02”]
  6. Bez dodatkowych bibliotek, tylko standardowa Java

Proszę bardzo...

public class NumUtils {
    /**
     * Transforms a string to an integer. If no numerical chars returns a String "0".
     *
     * @param str
     * @return retStr
     */
    static String makeToInteger(String str) {
        String s = str;
        double d;
        d = Double.parseDouble(makeToDouble(s));
        int i = (int) (d + 0.5D);
        String retStr = String.valueOf(i);
        System.out.printf(retStr + "   ");
        return retStr;
    }

    /**
     * Transforms a string to an double. If no numerical chars returns a String "0".
     *
     * @param str
     * @return retStr
     */
    static String makeToDouble(String str) {

        Boolean dotWasFound = false;
        String orgStr = str;
        String retStr;
        int firstDotPos = 0;
        Boolean negative = false;

        //check if str is null
        if(str.length()==0){
            str="0";
        }

        //check if first sign is "-"
        if (str.charAt(0) == '-') {
            negative = true;
        }

        //check if str containg any number or else set the string to '0'
        if (!str.matches(".*\\d+.*")) {
            str = "0";
        }

        //Replace ',' with '.'  (for some european users who use the ',' as decimal separator)
        str = str.replaceAll(",", ".");
        str = str.replaceAll("[^\\d.]", "");

        //Removes the any second dots
        for (int i_char = 0; i_char < str.length(); i_char++) {
            if (str.charAt(i_char) == '.') {
                dotWasFound = true;
                firstDotPos = i_char;
                break;
            }
        }
        if (dotWasFound) {
            String befDot = str.substring(0, firstDotPos + 1);
            String aftDot = str.substring(firstDotPos + 1, str.length());
            aftDot = aftDot.replaceAll("\\.", "");
            str = befDot + aftDot;
        }

        //Removes zeros from the begining
        double uglyMethod = Double.parseDouble(str);
        str = String.valueOf(uglyMethod);

        //Removes the .0
        str = str.replaceAll("([0-9])\\.0+([^0-9]|$)", "$1$2");

        retStr = str;

        if (negative) {
            retStr = "-"+retStr;
        }

        return retStr;

    }

    static boolean isNumeric(String str) {
        try {
            double d = Double.parseDouble(str);
        } catch (NumberFormatException nfe) {
            return false;
        }
        return true;
    }

}

5

Dopasowywanie wyrażeń regularnych

Oto kolejny przykład ulepszonego dopasowania wyrażenia regularnego „CraigTP” z większą liczbą sprawdzeń poprawności.

public static boolean isNumeric(String str)
{
    return str.matches("^(?:(?:\\-{1})?\\d+(?:\\.{1}\\d+)?)$");
}
  1. Dozwolony jest tylko jeden znak ujemny -i musi być na początku.
  2. Po znaku ujemnym musi być cyfra.
  3. Dozwolony jest tylko jeden znak dziesiętny ..
  4. Po znaku dziesiętnym musi być cyfra.

Test Regex

1                  --                   **VALID**
1.                 --                   INVALID
1..                --                   INVALID
1.1                --                   **VALID**
1.1.1              --                   INVALID

-1                 --                   **VALID**
--1                --                   INVALID
-1.                --                   INVALID
-1.1               --                   **VALID**
-1.1.1             --                   INVALID

5

Wyjątki są drogie, ale w tym przypadku RegEx trwa znacznie dłużej. Poniższy kod pokazuje prosty test dwóch funkcji - jednej z wyjątkami i drugiej z wyrażeniem regularnym. Na moim komputerze wersja RegEx jest 10 razy wolniejsza niż wyjątek.

import java.util.Date;


public class IsNumeric {

public static boolean isNumericOne(String s) {
    return s.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.      
}

public static boolean isNumericTwo(String s) {
    try {
        Double.parseDouble(s);
        return true;
    } catch (Exception e) {
        return false;
    }
}

public static void main(String [] args) {

    String test = "12345.F";

    long before = new Date().getTime();     
    for(int x=0;x<1000000;++x) {
        //isNumericTwo(test);
        isNumericOne(test);
    }
    long after = new Date().getTime();

    System.out.println(after-before);

}

}

Ogólnie rzecz biorąc, myślę, że ten rodzaj kodu byłby używany do sprawdzania takich rzeczy, jak wprowadzanie tekstu na klawiaturze. W takim przypadku szybkość nie jest brana pod uwagę, a robienie czegoś tak brzydkiego, jak rzucanie wyjątku w celu sprawdzenia liczby lub liczby nie jest błędne.
user872985,

Może nie. Wpisane dane są generalnie sprawdzane przez komponent interfejsu użytkownika, w którym błędy mogą być natychmiast pokazywane przed przesłaniem wartości. Częstsze może być sprawdzanie poprawności ciągów z dużych wejściowych plików tekstowych - tam, gdzie liczy się wydajność. Celem mojej odpowiedzi tutaj jest zajęcie się stwierdzeniem „wyjątki są powolne” w przyjętej odpowiedzi. Złożone wyrażenie regularne jest znacznie droższe. W moim kodzie nie ma w ogóle „brzydkiego rzutu” - to po prostu szybszy sposób wykrywania naruszeń. W metodzie „najpierw sprawdź, a następnie oblicz” dokonujesz dwóch przejść przez dane wejściowe: jeden do weryfikacji, a drugi do konwersji.
ChrisCantrell,

5

// sprawdź poniższy kod

public static boolean isDigitsOnly(CharSequence str) {
    final int len = str.length();
    for (int i = 0; i < len; i++) {
        if (!Character.isDigit(str.charAt(i))) {
            return false;
        }
    }
    return true;
}

Pytanie brzmi „numeryczny”, który może obejmować wartości niecałkowite.
rghome

3
// only int
public static boolean isNumber(int num) 
{
    return (num >= 48 && c <= 57); // 0 - 9
}

// is type of number including . - e E 
public static boolean isNumber(String s) 
{
    boolean isNumber = true;
    for(int i = 0; i < s.length() && isNumber; i++) 
    {
        char c = s.charAt(i);
        isNumber = isNumber & (
            (c >= '0' && c <= '9') || (c == '.') || (c == 'e') || (c == 'E') || (c == '')
        );
    }
    return isInteger;
}

// is type of number 
public static boolean isInteger(String s) 
{
    boolean isInteger = true;
    for(int i = 0; i < s.length() && isInteger; i++) 
    {
        char c = s.charAt(i);
        isInteger = isInteger & ((c >= '0' && c <= '9'));
    }
    return isInteger;
}

public static boolean isNumeric(String s) 
{
    try
    {
        Double.parseDouble(s);
        return true;
    }
    catch (Exception e) 
    {
        return false;
    }
}

3

To prosty przykład tego sprawdzenia:

public static boolean isNumericString(String input) {
    boolean result = false;

    if(input != null && input.length() > 0) {
        char[] charArray = input.toCharArray();

        for(char c : charArray) {
            if(c >= '0' && c <= '9') {
                // it is a digit
                result = true;
            } else {
                result = false;
                break;
            }
        }
    }

    return result;
}

3

Możesz użyć obiektu java.util.Scanner.

public static boolean isNumeric(String inputData) {
      Scanner sc = new Scanner(inputData);
      return sc.hasNextInt();
    }

2

Zmodyfikowałem rozwiązanie CraigTP, aby akceptować notację naukową oraz kropkę i przecinek jako separatory dziesiętne

^-?\d+([,\.]\d+)?([eE]-?\d+)?$

przykład

var re = new RegExp("^-?\d+([,\.]\d+)?([eE]-?\d+)?$");
re.test("-6546"); // true
re.test("-6546355e-4456"); // true
re.test("-6546.355e-4456"); // true, though debatable
re.test("-6546.35.5e-4456"); // false
re.test("-6546.35.5e-4456.6"); // false

2

Dlatego podoba mi się podejście Try * w .NET. Oprócz tradycyjnej metody analizy składni, która jest podobna do metody Java, dostępna jest również metoda TryParse. Nie jestem dobry w składni Java (parametry wyjściowe?), Dlatego proszę traktować poniższe elementy jako pseudo-kod. Powinno to jednak wyjaśnić tę koncepcję.

boolean parseInteger(String s, out int number)
{
    try {
        number = Integer.parseInt(myString);
        return true;
    } catch(NumberFormatException e) {
        return false;
    }
}

Stosowanie:

int num;
if (parseInteger("23", out num)) {
    // Do something with num.
}

tak, nie ma żadnych „parametrów wyjściowych” w Javie, a ponieważ opakowanie Integer jest niezmienne (dlatego nie może być użyte jako prawidłowe odwołanie do przechowywania danych wyjściowych), rozsądną opcją idiomatyczną byłoby zwrócenie obiektu Integer, który mógłby być zerowy, gdyby parsować nie powiodło się Opcją brzydszą może być przekazanie int [1] jako parametru wyjściowego.
fortran

Tak, pamiętam dyskusję o tym, dlaczego Java nie ma parametrów wyjściowych. ale zwracanie liczby całkowitej (w razie potrzeby zerowej) również byłoby w porządku, chociaż nie wiem o wydajności Javy w zakresie boksowania / rozpakowywania.
OregonGhost,

4
Lubię C # tak samo jak następny facet, ale nie ma sensu dodawać fragmentu kodu .NET C # dla pytania Java, gdy funkcje nie istnieją w Javie
Shane

Powoduje to powstanie problemu z sonarem, jeśli nie zarejestrujesz wyjątku
jmhostalet

2

Parsuj (tj. Za pomocą Integer#parseInt) i po prostu złap wyjątek. =)

Aby wyjaśnić: funkcja parseInt sprawdza, czy może parsować liczbę w każdym przypadku (oczywiście), a jeśli i tak chcesz ją przeanalizować, nie będziesz miał żadnego wpływu na wydajność poprzez wykonanie parsowania.

Jeśli nie chcesz go analizować (lub analizować bardzo, bardzo rzadko), możesz oczywiście zrobić to inaczej.


1
Kosztowne, jeśli stosowane w
szerokim zakresie

Powoduje to powstanie problemu z sonarem, jeśli nie zarejestrujesz wyjątku
jmhostalet

Double.parseDouble
Alex78191

2

Możesz użyć NumberUtils.isCreatable () z Apache Commons Lang .

Ponieważ NumberUtils.isNumber będzie przestarzałe w 4.0, więc użyj NumberUtils.isCreatable ().


2

Java 8 Stream, wyrażenie lambda, interfejs funkcjonalny

Wszystkie obsługiwane sprawy ( ciąg pusty, ciąg pusty itp. )

String someString = null; // something="", something="123abc", something="123123"

boolean isNumeric = Stream.of(someString)
            .filter(s -> s != null && !s.isEmpty())
            .filter(Pattern.compile("\\D").asPredicate().negate())
            .mapToLong(Long::valueOf)
            .boxed()
            .findAny()
            .isPresent();

2

Zilustrowałem niektóre warunki sprawdzania liczb i miejsc po przecinku bez użycia interfejsu API,

Sprawdź Fix Długość 1 cyfra

Character.isDigit(char)

Sprawdź numer stałej długości (zakładając, że długość wynosi 6)

String number = "132452";
if(number.matches("([0-9]{6})"))
System.out.println("6 digits number identified");

Sprawdź liczbę różnych długości między (Załóżmy, że długość 4 do 6)

//  {n,m}  n <= length <= m
String number = "132452";
if(number.matches("([0-9]{4,6})"))
System.out.println("Number Identified between 4 to 6 length");

String number = "132";
if(!number.matches("([0-9]{4,6})"))
System.out.println("Number not in length range or different format");

Sprawdź liczbę dziesiętną o zmiennej długości między (Załóżmy, że długość od 4 do 7)

//  It will not count the '.' (Period) in length
String decimal = "132.45";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "1.12";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "1234";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "-10.123";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "123..4";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

String decimal = "132";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

String decimal = "1.1";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

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


2

Opierając się na innych odpowiedziach, napisałem własną i nie używa wzorców ani parsowania ze sprawdzaniem wyjątków.

Sprawdza maksymalnie jeden znak minus i sprawdza maksymalnie jeden przecinek dziesiętny.

Oto kilka przykładów i ich wyników:

„1”, „-1”, „-1.5” i „-1.556” zwracają wartość true

„1..5”, „1A.5”, „1.5D”, „-” i „--1” zwracają wartość false

Uwaga: W razie potrzeby możesz zmodyfikować ten parametr, aby zaakceptować parametr Locale i przekazać go do wywołań DecimalFormatSymbols.getInstance () w celu użycia określonego ustawienia regionalnego zamiast bieżącego.

 public static boolean isNumeric(final String input) {
    //Check for null or blank string
    if(input == null || input.isBlank()) return false;

    //Retrieve the minus sign and decimal separator characters from the current Locale
    final var localeMinusSign = DecimalFormatSymbols.getInstance().getMinusSign();
    final var localeDecimalSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator();

    //Check if first character is a minus sign
    final var isNegative = input.charAt(0) == localeMinusSign;
    //Check if string is not just a minus sign
    if (isNegative && input.length() == 1) return false;

    var isDecimalSeparatorFound = false;

    //If the string has a minus sign ignore the first character
    final var startCharIndex = isNegative ? 1 : 0;

    //Check if each character is a number or a decimal separator
    //and make sure string only has a maximum of one decimal separator
    for (var i = startCharIndex; i < input.length(); i++) {
        if(!Character.isDigit(input.charAt(i))) {
            if(input.charAt(i) == localeDecimalSeparator && !isDecimalSeparatorFound) {
                isDecimalSeparatorFound = true;
            } else return false;
        }
    }
    return true;
}

1

Oto dwie metody, które mogą działać. (Bez użycia wyjątków). Uwaga: Java jest domyślnie wartością przekazywaną, a wartością ciągu jest adres danych obiektowych ciągu. Więc kiedy to robisz

stringNumber = stringNumber.replaceAll(" ", "");

Zmieniłeś wartość wejściową, tak aby nie miała spacji. Możesz usunąć tę linię, jeśli chcesz.

private boolean isValidStringNumber(String stringNumber)
{
    if(stringNumber.isEmpty())
    {
        return false;
    }

    stringNumber = stringNumber.replaceAll(" ", "");

    char [] charNumber = stringNumber.toCharArray();
    for(int i =0 ; i<charNumber.length ;i++)
    {
        if(!Character.isDigit(charNumber[i]))
        {
            return false;
        }
    }
    return true;
}

Oto inna metoda na wypadek, gdybyś chciał zezwolić na liczbę zmiennoprzecinkową. Ta metoda rzekomo pozwala na przekazanie liczb w formularzu 1,123,123,123,123,123.123 właśnie ją stworzyłem i myślę, że wymaga dalszych testów, aby upewnić się, że działa.

private boolean isValidStringTrueNumber(String stringNumber)
{
    if(stringNumber.isEmpty())
    {
        return false;
    }

    stringNumber = stringNumber.replaceAll(" ", "");
    int countOfDecimalPoint = 0;
    boolean decimalPointPassed = false;
    boolean commaFound = false;
    int countOfDigitsBeforeDecimalPoint = 0;
    int countOfDigitsAfterDecimalPoint =0 ;
    int commaCounter=0;
    int countOfDigitsBeforeFirstComma = 0;

    char [] charNumber = stringNumber.toCharArray();
    for(int i =0 ; i<charNumber.length ;i++)
    {
        if((commaCounter>3)||(commaCounter<0))
        {
            return false;
        }
        if(!Character.isDigit(charNumber[i]))//Char is not a digit.
        {
            if(charNumber[i]==',')
            {
                if(decimalPointPassed)
                {
                    return false;
                }
                commaFound = true;
                //check that next three chars are only digits.
                commaCounter +=3;
            }
            else if(charNumber[i]=='.')
            {
                decimalPointPassed = true;
                countOfDecimalPoint++;
            }
            else
            {
                return false;
            }
        }
        else //Char is a digit.
        {
            if ((commaCounter>=0)&&(commaFound))
            {
                if(!decimalPointPassed)
                {
                    commaCounter--;
                }
            }

            if(!commaFound)
            {
                countOfDigitsBeforeFirstComma++;
            }

            if(!decimalPointPassed)
            {
                countOfDigitsBeforeDecimalPoint++;
            }
            else
            {
                countOfDigitsAfterDecimalPoint++;
            }
        }
    }
    if((commaFound)&&(countOfDigitsBeforeFirstComma>3))
    {
        return false;
    }
    if(countOfDecimalPoint>1)
    {
        return false;
    }

    if((decimalPointPassed)&&((countOfDigitsBeforeDecimalPoint==0)||(countOfDigitsAfterDecimalPoint==0)))
    {
        return false;
    }
    return true;
}

Och, dobre pytanie. Myślę, że ten działa tylko na liczbach całkowitych normalnego typu. Metoda została początkowo stworzona do filtrowania wejściowych numerów telefonów i zliczania numerów.
XForCE07
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.