Como verificar se uma String é numérica em Java

Como você verificaria se uma String era um número antes de analisá-la?

Com Apache Commons Lang 3.5 e acima: NumberUtils.isCreatable ou StringUtils.isNumeric .

Com Apache Commons Lang 3.4 e abaixo: NumberUtils.isNumber ou StringUtils.isNumeric .

Você também pode usar StringUtils.isNumericSpace que retorna true para strings vazias e ignora espaços internos na string. (Os javadocs vinculados contêm exemplos detalhados para cada método.)

Geralmente, isso é feito com uma function simples definida pelo usuário (ou seja, function Roll-your-own “isNumeric”).

Algo como:

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

No entanto, se você está chamando muito essa function e espera que muitas das verificações falhem devido a não ser um número, o desempenho desse mecanismo não será grande, já que você está confiando em exceções sendo lançadas para cada falha, que é uma operação bastante cara.

Uma abordagem alternativa pode ser usar uma expressão regular para verificar a validade de ser um número:

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

Tenha cuidado com o mecanismo RegEx acima, pois ele falhará se você estiver usando dígitos não-arábicos (ou seja, números diferentes de 0 a 9). Isso ocorre porque a parte “\ d” do RegEx corresponderá apenas a [0-9] e efetivamente não é internacionalmente numericamente ciente. (Obrigado ao OregonGhost por apontar isso!)

Ou até mesmo outra alternativa é usar o object java.text.NumberFormat integrado do Java para ver se, após a análise da sequência, a posição do analisador está no final da sequência. Se for, podemos assumir que a string inteira é numérica:

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

se você está no android, então você deve usar:

 android.text.TextUtils.isDigitsOnly(CharSequence str) 

documentação pode ser encontrada aqui

mantenha isso simples . a maioria das pessoas pode “reprogramar” (a mesma coisa).

Como o @CraigTP mencionou em sua excelente resposta, também tenho preocupações de desempenho semelhantes sobre o uso de exceções para testar se a string é numérica ou não. Então acabei dividindo a string e use java.lang.Character.isDigit() .

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

De acordo com o Javadoc , Character.isDigit(char) reconhecerá corretamente dígitos não latinos. Em termos de desempenho, acho que um simples número N de comparações em que N é o número de caracteres na string seria mais computacionalmente eficiente do que fazer uma correspondência de expressão regular.

ATUALIZAÇÃO: Como apontado por Jean-François Corbett no comentário, o código acima só validaria números inteiros positivos, o que cobre a maioria do meu caso de uso. Abaixo está o código atualizado que valida corretamente os números decimais de acordo com a localidade padrão usada em seu sistema, com a suposição de que o separador decimal só ocorre uma vez na string.

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

Expressões lambda do Java 8.

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

A biblioteca Guava do Google fornece um bom método auxiliar para fazer isso: Ints.tryParse . Você o usa como Integer.parseInt mas retorna null vez de lançar uma exceção se a string não for analisada para um inteiro válido. Note que ele retorna Integer, não int, então você tem que converter / autobox ele de volta para int.

Exemplo:

 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 

No entanto, a partir do lançamento atual – Guava r11 – ainda está marcado @Beta.

Eu não fiz benchmarking. Olhando para o código-fonte, há alguma sobrecarga de muita verificação de Character.digit(string.charAt(idx)) mas no final eles usam Character.digit(string.charAt(idx)) , semelhante, mas ligeiramente diferente, da resposta de @Ibrahim acima. Não há exceção lidando com sobrecarga sob as capas em sua implementação.

Não use exceções para validar seus valores. Use util libs ao invés do apache NumberUtils:

 NumberUtils.isNumber(myStringValue); 

Editar :

Por favor note que, se a sua string começar com um 0, NumberUtils irá interpretar o seu valor como hexadecimal.

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

Por que todos estão pressionando por soluções de exceção / regex?

Embora eu possa entender que a maioria das pessoas está bem com o uso de try / catch, se você quiser fazer isso com freqüência … pode ser extremamente desgastante.

O que fiz aqui foi pegar o regex, os methods parseNumber () e o método de busca de array para ver qual era o mais eficiente. Desta vez, eu só olhei para números inteiros.

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

Os resultados em velocidade que recebi foram:

 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: Eu não estou afirmando que esses methods são 100% otimizados, eles são apenas para demonstração dos dados

Exceções ganhas se e somente se o número for de 4 caracteres ou menos, e cada string for sempre um número ... e nesse caso, por que ter um cheque?

Em suma, é extremamente doloroso se você encontrar números inválidos frequentemente com o try / catch, o que faz sentido. Uma regra importante que eu sempre sigo NUNCA é usar try / catch para o stream do programa . Este é um exemplo porque.

Curiosamente, o simples se char <0 || > 9 foi extremamente simples de escrever, fácil de lembrar (e deve funcionar em vários idiomas) e ganha quase todos os cenários de teste.

A única desvantagem é que eu estou supondo que Integer.parseInt () pode manipular números não-ASCII, enquanto o método de busca de array não.


Para aqueles que estão se perguntando por que eu disse que é fácil lembrar o array de personagens, se você sabe que não há sinais negativos, você pode facilmente se livrar de algo condensado como este:

 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; 

Por último, como uma nota final, fiquei curioso sobre o operador de assgment no exemplo aceito com todos os votos para cima. Adicionando a atribuição de

 double d = Double.parseDouble(...) 

não é apenas inútil, já que você nem usa o valor, mas desperdiça tempo de processamento e aumenta o tempo de execução em alguns nanossegundos (o que levou a um aumento de 100-200 ms nos testes). Não vejo por que alguém faria isso, pois na verdade é um trabalho extra para reduzir o desempenho.

Você acha que isso seria otimizado ... embora talvez eu deva verificar o código de bytes e ver o que o compilador está fazendo. Isso não explica por que sempre me pareceu mais demorado para mim, se de alguma forma for otimizado ... portanto, eu me pergunto o que está acontecendo. Como nota: Por mais tempo, quero dizer executar o teste para 10000000 iterações, e executar esse programa várias vezes (10x +) sempre mostrou que é mais lento.

EDIT: atualizado um teste para Character.isDigit ()

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

A expressão regular de CraigTP (mostrada acima) produz alguns falsos positivos. Por exemplo, “23y4” será contado como um número porque “.” corresponde a qualquer caractere que não seja o ponto decimal.

Também rejeitará qualquer número com um ‘+’ líder

Uma alternativa que evita esses dois pequenos problemas é

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

Você pode usar NumberFormat#parse :

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

Se você estiver usando java para desenvolver o aplicativo Android, você pode usar a function TextUtils.isDigitsOnly .

Aqui estava a minha resposta para o problema.

Um método catch all convenience que você pode usar para analisar qualquer String com qualquer tipo de analisador: isParsable(Object parser, String str) . O analisador pode ser uma Class ou um object . Isso também permite que você use analisadores personalizados que você escreveu e deve funcionar para qualquer cenário, por exemplo:

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

Aqui está meu código completo com descrições de método.

 import java.lang.reflect.*; /** * METHOD: isParsable

* * This method will look through the methods of the specified from parameter * looking for a public method name starting with "parse" which has only one String * parameter.

* * The parser parameter can be a class or an instantiated object, eg: * Integer.class or new Integer(1). If you use a * Class type then only static methods are considered.

* * When looping through potential methods, it first looks at the Class associated * with the parser 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.

* * This method will hide any normal parse exceptions, but throws any exceptions due to * programmatic errors, eg: NullPointerExceptions, etc. If you specify a parser * parameter which has no matching parse methods, a NoSuchMethodException will be thrown * embedded within a RuntimeException.

* * Example:
* isParsable(Boolean.class, "true");
* 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");
*

* * @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

* * A convienence method which calls the isParseable method, but does not throw any exceptions * which could be thrown through programatic errors.

* * Use of {@link #isParseable(Object, String) isParseable} is recommended for use so programatic * errors can be caught in development, unless the value of the parser parameter is * unpredictable, or normal programtic exceptions should be ignored.

* * See {@link #isParseable(Object, String) isParseable} for full description of method * usability.

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

Para corresponder apenas a inteiros positivos de base-dez, que contém apenas dígitos ASCII, use:

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

Aqui está a minha class para verificar se uma string é numérica. Também corrige sequências numéricas:

Características:

  1. Remove zeros desnecessários [“12.0000000” -> “12”]
  2. Remove zeros desnecessários [“12.0580000” -> “12.058”]
  3. Remove caracteres não numéricos [“12.00sdfsdf00” -> “12”]
  4. Suporta valores de string negativos [“-12,020000” -> “-12,02”]
  5. Remove vários pontos [“-12.0.20.000” -> “-12.02”]
  6. Nenhuma biblioteca extra, apenas Java padrão

Aqui está…

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

As exceções são caras, mas neste caso o RegEx leva muito mais tempo. O código abaixo mostra um teste simples de duas funções – uma usando exceções e outra usando regex. Na minha máquina, a versão RegEx é 10 vezes mais lenta que a exceção.

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

Uma abordagem de bom desempenho evitando try-catch e lidando com números negativos e notação científica.

 Pattern PATTERN = Pattern.compile( "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E\\d+)?$" ); public static boolean isNumeric( String value ) { return value != null && PATTERN.matcher( value ).matches(); } 

Correspondência Regex

Aqui está outro exemplo de regex “CraigTP” atualizado com mais validações.

 public static boolean isNumeric(String str) { return str.matches("^(?:(?:\\-{1})?\\d+(?:\\.{1}\\d+)?)$"); } 
  1. Apenas um sinal negativo - permitido e deve estar no começo.
  2. Após o sinal negativo, deve haver um dígito.
  3. Apenas um sinal decimal . permitido.
  4. Após o sinal decimal, deve haver um dígito.

Teste de 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 

// por favor, verifique abaixo o código

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

É por isso que gosto da abordagem Try * no .NET. Além do método Parse tradicional que é como o Java, você também tem um método TryParse. Eu não sou bom na syntax Java (out parâmetros?), Por favor, trate o seguinte como algum tipo de pseudo-código. Deve fazer o conceito claro embora.

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

Este é um exemplo simples para essa verificação:

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

Analise-o (isto é, com Integer#parseInt ) e simplesmente capture a exceção. =)

To clarify: The parseInt function checks if it can parse the number in any case (obviously) and if you want to parse it anyway, you are not going to take any performance hit by actually doing the parsing.

If you would not want to parse it (or parse it very, very rarely) you might wish to do it differently of course.

You can use the java.util.Scanner object.

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

Here are two methods that might work. (Without using Exceptions). Note : Java is a pass-by-value by default and a String’s value is the address of the String’s object data. So , when you are doing

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

You have changed the input value to have no spaces. You can remove that line if you want.

 private boolean isValidStringNumber(String stringNumber) { if(stringNumber.isEmpty()) { return false; } stringNumber = stringNumber.replaceAll(" ", ""); char [] charNumber = stringNumber.toCharArray(); for(int i =0 ; i 

Here is another method in case you want to allow floats This method allegedly allows numbers in the form to pass 1,123,123,123,123,123.123 i have just made it , and i think it needs further testing to ensure it is working.

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

I modified CraigTP’s solution to accept scientific notation and both dot and comma as decimal separators as well

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

example

 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 

You can use NumberUtils.isCreatable() from Apache Commons Lang .

Since NumberUtils.isNumber will be deprecated in 4.0, so use NumberUtils.isCreatable() instead.

This is the fastest way i know to check if String is Number or not:

 public static boolean isNumber(String str){ int i=0, len=str.length(); boolean a=false,b=false,c=false, d=false; if(i 

Parallel checking for very long strings using IntStream

In Java 8, the following tests if all characters of the given string are within ‘0’ to ‘9’. Mind that the empty string is accepted:

 string.chars().unordered().parallel().allMatch( i -> '0' <= i && '9' >= i ) 

I think the only way to reliably tell if a string is a number, is to parse it. So I would just parse it, and if it’s a number, you get the number in an int for free!

You could use BigDecimal if the string may contain decimals:

 try { new java.math.BigInteger(testString); } catch(NumberFormatException e) { throw new RuntimeException("Not a valid number"); }