Come verificare se una stringa è numerica in Java


887

Come verifichi se una stringa fosse un numero prima di analizzarlo?


36
Tutte le soluzioni proposte con espressioni regolari non funzioneranno con numeri esadecimali.
Oscar Castiblanco,

e passare una stringa nulla nelle corrispondenze (...) genererà un'eccezione NullPointer.
Hitesh Sahu

Vedi la risposta di Max Malysh per una concisa soluzione Java 8 senza librerie di terze parti.
Andy Thomas,

Le stringhe null di @HiteshSahu sembrano essere gestite con grazia nell'ultima versione (inclusi Java 6.xe 7.x)
lifebalance

Tutte le soluzioni proposte da utilizzare Integer.parseInt()non riescono ad analizzare i numeri di cellulare NumberFormatException.
Non un bug

Risposte:


691

Con Apache Commons Lang 3.5 e versioni successive: NumberUtils.isCreatableo StringUtils.isNumeric.

Con Apache Commons Lang 3.4 e precedenti: NumberUtils.isNumbero StringUtils.isNumeric.

Puoi anche usare StringUtils.isNumericSpacequali restituisce truestringhe vuote e ignora gli spazi interni nella stringa. Un altro modo è usare NumberUtils.isParsableche sostanzialmente controlla che il numero sia analizzabile secondo Java. (I javadocs collegati contengono esempi dettagliati per ciascun metodo.)


59
StringUtils.isNumeric()probabilmente non sarebbe appropriato qui poiché controlla solo se la stringa è una sequenza di cifre. Andrebbe bene per la maggior parte degli ints, ma non così per i numeri con decimali, separatori di gruppi, ecc.
Jeff Mercado

42
reinventare la ruota perché non si include un'intera libreria perché è necessaria una funzione a 3 righe in un unico posto.
dalvarezmartinez1

12
Vale davvero la pena aggiungere un'intera libreria per questa funzione? Ovviamente se usato con altre cose è fantastico, ma probabilmente è eccessivo considerando che le persone hanno risolto questo problema in una riga di codice.
Acqua il

7
Non funziona con i negativi. E metà di tutti i numeri sono negativi, quindi .....
Paul Draper,

6
@PaulDraper: hai ragione, StringUtilsnon supporta i segni guida ma dovresti controllare NumberUtils.isCreatable, supporta correttamente i negativi.
palacsint,

904

Questo viene generalmente fatto con una semplice funzione definita dall'utente (ad es. Funzione "isNumeric" di Roll-your-own).

Qualcosa di simile a:

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

Tuttavia, se stai chiamando questa funzione molto e ti aspetti che molti dei controlli falliscano a causa del fatto che non sono un numero, l'esecuzione di questo meccanismo non sarà eccezionale, dal momento che fai affidamento sulle eccezioni generate per ogni errore, che è un'operazione piuttosto costosa.

Un approccio alternativo potrebbe essere quello di utilizzare un'espressione regolare per verificare la validità di essere un numero:

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

Fai attenzione al meccanismo RegEx sopra, tuttavia, poiché fallirà se stai usando cifre non arabe (cioè numeri diversi da 0 a 9). Questo perché la parte "\ d" di RegEx corrisponderà solo a [0-9] e di fatto non è a livello internazionale consapevole numericamente. (Grazie a OregonGhost per averlo segnalato!)

O anche un'altra alternativa è usare l'oggetto java.text.NumberFormat incorporato di Java per vedere se, dopo aver analizzato la stringa, la posizione del parser è alla fine della stringa. In tal caso, possiamo supporre che l'intera stringa sia numerica:

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
\ D in Java Regex corrisponde solo a cifre latine? Se è come le regex di .NET, riscontrerai
OregonGhost

3
la soluzione numberFormatter è probabilmente solo leggermente migliore di quella di NumberFormatException. Sospetto che il modo migliore sia usare regex one.
Chii,

11
Nota che .nella tua regex corrisponderà a qualsiasi carattere, non solo al carattere separatore decimale.
jqno,

9
+1 per rendersi conto delle spese di try / catch. Questo è in realtà un approccio orribile da usare a lungo termine per un uso ripetuto, ma in realtà ne siamo bloccati in Java.
demongolem,

5
Si noti che non esistono cose come "numeri latini" e i numeri 0-9 sono in realtà numeri arabi. Le persone probabilmente hanno familiarità con i numeri romani, che venivano usati da persone che parlavano latino, nella forma I, II, III, IV, V, VI, ecc. En.wikipedia.org/wiki/Arabic_numerals ; en.wikipedia.org/wiki/Roman_numerals
dantiston

152

se sei su Android, allora dovresti usare:

android.text.TextUtils.isDigitsOnly(CharSequence str)

la documentazione è disponibile qui

mantienilo semplice . per lo più tutti possono "riprogrammare" (la stessa cosa).


4
@ kape123 :) certo che "123.456" non contiene cifre.
Ahmed Alejo,

8
Nota: questo risulta in NPE per input null. Inoltre, non funziona con numeri negativi o decimali.
gMale

2
Mi piace!! Penso che questo sia assolutamente per le cifre. Non per .,-
illusionJJ

Questo è proprio quello che stavo cercando. Qualcosa di semplice da controllare per solo cifre 0-9. Ho impostato un filtro nella dichiarazione del mio EditText, ma nel caso in cui venga cambiato o sostituito lungo la strada è bello avere anche un semplice controllo programmatico.
jwehrle,

127

Java 8 espressioni lambda.

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

4
Puoi anche usare un riferimento al metodo: someString.chars (). AllMatch (Character :: isDigit)
Wienczny

3
Bello ma continua a reinventare la ruota come quasi tutte le "soluzioni" qui. Inoltre, non riesce su "null" (come quasi tutti gli altri).
qben

8
Questa risposta è concisa, semplice e leggibile. Puoi quasi leggerlo come in inglese - "tutti i caratteri corrispondono a cifre". Non richiede librerie di terze parti. Non utilizza eccezioni in casi non eccezionali. Questa dovrebbe diventare la risposta accettata.
Andy Thomas,

14
Cosa produrrà per "-1"?
Balázs Németh,

2
Non è la risposta giusta. Una stringa numerica può avere caratteri non numerici (es. "." O "-") ed essere comunque perfettamente numerica. Ad esempio 0,5, -1 e 1.000 falliranno tutti con questa risposta e tuttavia sono perfettamente numerici.
Simeon G

126

Come aveva citato @CraigTP nella sua eccellente risposta, ho anche problemi di prestazioni simili sull'uso delle Eccezioni per verificare se la stringa è numerica o meno. Quindi finisco per dividere la stringa e usare java.lang.Character.isDigit().

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

Secondo il Javadoc , Character.isDigit(char)riconoscerà correttamente le cifre non latine. Per quanto riguarda le prestazioni, penso che un semplice numero N di confronti in cui N è il numero di caratteri nella stringa sarebbe più efficiente dal punto di vista computazionale rispetto a una corrispondenza regex.

AGGIORNAMENTO: Come sottolineato da Jean-François Corbett nel commento, il codice sopra riportato convaliderebbe solo numeri interi positivi, che copre la maggior parte del mio caso d'uso. Di seguito è riportato il codice aggiornato che convalida correttamente i numeri decimali in base alle impostazioni internazionali predefinite utilizzate nel sistema, presupponendo che il separatore decimale si presenti solo una volta nella stringa.

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
Il separatore decimale non causerà anche questo errore?
Jean-François Corbett,

1
@ Jean-FrançoisCorbett: buon punto, ho aggiornato il codice con uno più recente che accetta i separatori decimali.
Ibrahim Arief

2
-Ve sign fallisce questa funzione?
java_mouse,

3
La chiamata toCharArray()creerà una copia dell'array nell'oggetto String perché le stringhe sono immutabili. Probabilmente più veloce da usare direttamente il charAt(int index)metodo sull'oggetto String.
Mike Kucera,

2
Genererà StringIndexOutOfBoundsExceptionquando passa una stringa di lunghezza 0. Può essere fissato conif(str.length() == 0) return false;
samgak

43

Biblioteca Guava di Google offre un metodo di supporto piacevole per fare questo: Ints.tryParse. Lo usi come, Integer.parseIntma restituisce nullanziché generare un'eccezione se la stringa non analizza un numero intero valido. Nota che restituisce Integer, non int, quindi devi convertirlo / autobox in int.

Esempio:

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

Tuttavia, a partire dalla versione corrente - Guava r11 - è ancora contrassegnato con @Beta.

Non l'ho confrontato. Guardando il codice sorgente c'è un certo sovraccarico da molti controlli di sanità mentale ma alla fine usano Character.digit(string.charAt(idx)), simile, ma leggermente diverso dalla risposta di @Ibrahim sopra. Non vi è alcuna eccezione nella gestione delle spese generali sotto le coperte nella loro implementazione.


Attenzione che ciò genererebbe NPE nel caso in cui l'argomento sia nullo.
Vadzim,

30

Non utilizzare le eccezioni per convalidare i valori. Usa le librerie Util invece di apache NumberUtils:

NumberUtils.isNumber(myStringValue);

Modifica :

Si noti che, se la stringa inizia con uno 0, NumberUtils interpreterà il valore come esadecimale.

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

7
La risposta accettata, tre anni prima, era già coperta Number.isNumber().
Andy Thomas,

Io non la penso così. È stato aggiornato o ha cambiato la risposta accettata. Ricordo che la risposta accettata non riguardava NumberUtils, ecco perché ho aggiunto la mia risposta. Ma grazie per il commento
Goot

2
@Goot - La cronologia della risposta accettata mostra che Number.isNumber()era presente dalla prima versione della risposta, datata 24 settembre 12 alle 17:01.
Andy Thomas,

@Goot, questo è abbastanza buono in quanto copre anche il controllo del valore decimale, a differenza di StringUtils.
Heena Hussain,

24

Perché tutti spingono per soluzioni di eccezione / regex?

Anche se riesco a capire che la maggior parte delle persone sta bene usando try / catch, se vuoi farlo frequentemente ... può essere estremamente faticoso.

Quello che ho fatto qui è stato prendere il regex, i metodi parseNumber () e il metodo di ricerca dell'array per vedere quale era il più efficiente. Questa volta, ho solo guardato i numeri interi.

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

I risultati nella velocità che ho ottenuto sono stati:

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

Disclaimer: non sto affermando che questi metodi sono ottimizzati al 100%, sono solo per la dimostrazione dei dati

Eccezioni vinte se e solo se il numero è composto da 4 caratteri o meno, e ogni stringa è sempre un numero ... nel qual caso, perché anche avere un controllo?

In breve, è estremamente doloroso imbattersi frequentemente in numeri non validi con il tentativo / cattura, il che ha senso. Una regola importante che seguo sempre è MAI utilizzare try / catch per il flusso del programma . Questo è un esempio del perché.

È interessante notare che il semplice se char <0 || > 9 è stato estremamente semplice da scrivere, facile da ricordare (e dovrebbe funzionare in più lingue) e vince quasi tutti gli scenari di test.

L'unico aspetto negativo è che suppongo che Integer.parseInt () potrebbe gestire numeri non ASCII, mentre il metodo di ricerca dell'array non lo fa.


Per quelli che si chiedono perché ho detto che è facile ricordare l'array di personaggi uno, se sai che non ci sono segni negativi, puoi facilmente scappare con qualcosa di condensato come questo:

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;

Infine, come nota finale, ero curioso dell'operatore incaricato nell'esempio accettato con tutti i voti in su. Aggiungendo nell'assegnazione di

double d = Double.parseDouble(...)

non è solo inutile poiché non si utilizza nemmeno il valore, ma si spreca il tempo di elaborazione e aumenta il tempo di esecuzione di alcuni nanosecondi (il che ha portato a un aumento di 100-200 ms nei test). Non riesco a capire perché qualcuno dovrebbe farlo dal momento che in realtà è un lavoro extra per ridurre le prestazioni.

Penseresti che sarebbe ottimizzato ... anche se forse dovrei controllare il bytecode e vedere cosa sta facendo il compilatore. Questo non spiega perché mi sia sempre apparso più lungo, anche se in qualche modo è stato ottimizzato ... quindi mi chiedo cosa stia succedendo. Come nota: per più lungo, intendo eseguire il test per 10000000 iterazioni e l'esecuzione di quel programma più volte (10x +) ha sempre mostrato che è più lento.

EDIT: aggiornato un test per Character.isDigit ()


4
Questo non compila una nuova espressione regolare ogni volta? Non sembra molto efficiente.
Samuel Edwin Ward l'

1
@SamuelEdwinWard Questo è il tutto il motivo per cui ho fatto questo post ... l'esempio espressione regolare utilizzata risposte fornite di altre persone e ha dimostrato come sia inefficiente. Anche se si tenta di regex con la pre-compilazione anticipata e solo l'utilizzo, le differenze di tempo sono: 2587 ms per la regex che ho postato da altre persone fornite, 950 ms quando compilata in anticipo, 144 ms quando la si fa come una matrice numerica (per 1 mil iterazioni della stessa stringa). Compilare in anticipo ovviamente aiuterebbe, ma purtroppo è ancora abbastanza inferiore al modo array ... a meno che non ci sia qualche pazza ottimizzazione che non conosco.
Acqua

Credere che Regex renda le cose più veloci è quasi un errore. Se è una ricerca una tantum, sì, ho capito ... ma ho notato che il codice scritto in modo efficace supera in realtà regex abbastanza da scioccarti! Ottimo post @Water
Yo Apps

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

L'espressione regolare di CraigTP (mostrata sopra) produce alcuni falsi positivi. Ad esempio "23y4" verrà conteggiato come un numero perché "." corrisponde a qualsiasi carattere non al punto decimale.

Inoltre rifiuterà qualsiasi numero con un '+' iniziale

Un'alternativa che evita questi due problemi minori è

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

questo tornerà trueper un singolo più "+"o meno "-", e falseper"0."
user85421

Buona cattura del singolo più o meno. È "0" un numero valido?
user872985

"0."è valido per Double.parseDouble()ed è un letterale valido secondo JLS ( §3.10.2 )!
user85421

Anche la creazione di espressioni regolari è costosa. L'espressione regolare deve essere creata una volta e riutilizzata
Daniel Nuriyev,

1
dovresti cambiarlo inmatches("-?\\d+([.]\\d+)?")
Bobs

14

Possiamo provare a sostituire tutti i numeri della stringa specificata con ("") ovvero uno spazio vuoto e se dopo che la lunghezza della stringa è zero, allora possiamo dire che la stringa data contiene solo numeri. [Se hai trovato utile questa risposta, prendi in considerazione la possibilità di votarla] Esempio:

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

Quindi, ""è un numero, ma "3.14"e "-1"non sono?
Eric Duminil,

Evidentemente non si applica a tutti i moduli numerici, ma ecco un voto per pensare diversamente ... se il pensiero originale era tuo, cioè.
gbenroscience,

12

Puoi usare NumberFormat#parse:

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

Offerto una modifica - .getInstance () mancava. +1 poiché questa è stata la risposta con cui ho trovato questa domanda.
8bitjunkie,

5
Costoso se usato estesamente
Daniel Nuriyev,

1
Passerà anche se ci sono personaggi spazzatura alla fine di value.
Brian White,


1
Questo ha funzionato per il formato numerico 0x0001 in cui Double.parseDouble non funzionava. +1
Spigola77


8

Ecco la mia risposta al problema.

Una cattura tutte metodo comodo che può essere utilizzato per analizzare qualsiasi stringa con qualsiasi tipo di parser: isParsable(Object parser, String str). Il parser può essere un Classo un object. Questo ti consentirà anche di utilizzare parser personalizzati che hai scritto e dovrebbe funzionare per qualsiasi scenario, ad esempio:

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

Ecco il mio codice completo con le descrizioni dei metodi.

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

Per abbinare solo numeri interi base-dieci positivi, che contiene solo cifre ASCII, utilizzare:

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

5

Un approccio efficace che evita di provare e gestire numeri negativi e notazione scientifica.

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

Ecco la mia classe per verificare se una stringa è numerica. Corregge anche stringhe numeriche:

Caratteristiche:

  1. Rimuove gli zeri non necessari ["12.0000000" -> "12"]
  2. Rimuove gli zeri non necessari ["12.0580000" -> "12.058"]
  3. Rimuove i caratteri non numerici ["12.00sdfsdf00" -> "12"]
  4. Gestisce valori di stringa negativi ["-12,020000" -> "-12,02"]
  5. Rimuove più punti ["-12.0.20.000" -> "-12.02"]
  6. Nessuna libreria extra, solo Java standard

Ecco qui...

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

Abbinamento Regex

Ecco un altro esempio di corrispondenza regex "CraigTP" aggiornata con più convalide.

public static boolean isNumeric(String str)
{
    return str.matches("^(?:(?:\\-{1})?\\d+(?:\\.{1}\\d+)?)$");
}
  1. È -consentito solo un segno negativo e deve essere all'inizio.
  2. Dopo il segno negativo ci deve essere una cifra.
  3. È .consentito solo un segno decimale .
  4. Dopo il segno decimale ci deve essere una cifra.

Test di 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

Le eccezioni sono costose, ma in questo caso il RegEx impiega molto più tempo. Il codice seguente mostra un semplice test di due funzioni: una che utilizza eccezioni e una che utilizza regex. Sulla mia macchina la versione RegEx è 10 volte più lenta dell'eccezione.

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

}

}

In generale, penso che questo tipo di codice verrebbe usato per controllare cose come input digitato. In quel caso la velocità non è una considerazione e fare qualcosa di brutto come lanciare un'eccezione per verificare il numero o il non-numero è sbagliato.
user872985

Forse no. L'input tipizzato viene generalmente verificato dal componente UI in cui è possibile visualizzare immediatamente gli errori prima di inviare il valore. Potrebbe essere più comune convalidare le stringhe da file di testo di input di grandi dimensioni, dove le prestazioni sono importanti. L'obiettivo nella mia risposta qui è di affrontare l'affermazione "le eccezioni sono lente" nella risposta accettata. La regex complessa è molto più costosa. E non c'è affatto "brutti passi" nel mio codice - solo un modo più veloce per rilevare le violazioni. Con un approccio check-first-then-calcular fai due passaggi attraverso l'input: uno per verificare e poi un altro per convertire.
ChrisCantrell,

5

// controlla sotto il codice

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

La domanda dice "numerico" che potrebbe includere valori non interi.
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

Questo è un semplice esempio per questo controllo:

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

È possibile utilizzare l'oggetto java.util.Scanner.

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

2

Ho modificato la soluzione di CraigTP per accettare anche la notazione scientifica e sia il punto che la virgola come separatori decimali

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

esempio

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

Ecco perché mi piace l'approccio Try * in .NET. Oltre al metodo Parse tradizionale simile a quello Java, hai anche un metodo TryParse. Non sono bravo nella sintassi Java (parametri out?), Quindi ti preghiamo di considerare quanto segue come una specie di pseudo-codice. Dovrebbe rendere chiaro il concetto però.

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

Uso:

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

sì, non ci sono "parametri out" in Java e poiché il wrapper Integer è immutabile (quindi non può essere usato come riferimento valido per memorizzare l'output), l'opzione idiomatica ragionevole sarebbe quella di restituire un oggetto Integer che potrebbe essere nullo se l'analisi fallito. Un'opzione più brutta potrebbe essere quella di passare un int [1] come parametro di output.
fortran,

Sì, ricordo una discussione sul perché Java non ha parametri di output. ma restituire un intero (come nullo, se necessario) andrebbe bene, immagino, anche se non conosco le prestazioni di Java per quanto riguarda il boxing / unboxing.
OregonGhost

4
Mi piace C # tanto quanto il prossimo, ma è inutile aggiungere uno snippet di codice .NET C # per una domanda Java quando le funzionalità non esistono in Java
Shane,

Si creerebbe un problema con il sonar se non si registra l'eccezione
jmhostalet,

2

Analizzalo (cioè con Integer#parseInt ) e prendi semplicemente l'eccezione. =)

Per chiarire: la funzione parseInt verifica se è in grado di analizzare il numero in ogni caso (ovviamente) e se si desidera analizzarlo comunque, non si subirà alcun hit di prestazione eseguendo effettivamente l'analisi.

Se non si desidera analizzarlo (o analizzarlo molto, molto raramente), si potrebbe desiderare di farlo in modo diverso, ovviamente.


1
Costoso se usato estensivamente
Daniel Nuriyev,

Si creerebbe un problema con il sonar se non si registra l'eccezione
jmhostalet,

Double.parseDouble
Alex78191

2

Puoi usare NumberUtils.isCreatable () da Apache Commons Lang .

Poiché NumberUtils.isNumber sarà deprecato in 4.0, utilizzare invece NumberUtils.isCreatable ().


2

Java 8 Stream, espressione lambda, interfaccia funzionale

Tutti i casi gestiti ( stringa null, stringa vuota ecc. )

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

Ho illustrato alcune condizioni per controllare numeri e decimali senza utilizzare alcuna API,

Controllare la lunghezza fissa Numero di 1 cifra

Character.isDigit(char)

Controlla il numero di lunghezza fissa (supponiamo che la lunghezza sia 6)

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

Controlla il numero della lunghezza variabile tra (Supponi da 4 a 6 lunghezze)

//  {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");

Controlla il numero decimale della lunghezza variabile tra (Supponiamo che la lunghezza sia compresa tra 4 e 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");

Spero che possa aiutare molti.


2

Sulla base di altre risposte che ho scritto il mio e non utilizza modelli o analisi con controllo delle eccezioni.

Verifica un massimo di un segno meno e verifica un massimo di un punto decimale.

Ecco alcuni esempi e i loro risultati:

"1", "-1", "-1.5" e "-1.556" restituiscono true

"1..5", "1A.5", "1.5D", "-" e "--1" restituiscono false

Nota: se necessario, è possibile modificarlo per accettare un parametro Locale e passarlo nelle chiamate DecimalFormatSymbols.getInstance () per utilizzare una Locale specifica anziché quella corrente.

 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

Ecco due metodi che potrebbero funzionare. (Senza utilizzare le eccezioni). Nota: Java è un valore passa per impostazione predefinita e il valore di una stringa è l'indirizzo dei dati dell'oggetto della stringa. Quindi, quando lo stai facendo

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

Hai modificato il valore di input per non avere spazi. Puoi rimuovere quella linea se vuoi.

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

Ecco un altro metodo nel caso in cui si desideri consentire i float Questo metodo presumibilmente consente ai numeri nel modulo di passare 1.123.123.123.123.123.123 l'ho appena fatto e penso che abbia bisogno di ulteriori test per assicurarsi che funzioni.

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

Oh, bella domanda Immagino che questo funzioni solo con interi di tipo normale. Il metodo è stato inizialmente creato per filtrare i numeri di telefono di input e contare i numeri.
XForCE07
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.