validar una solo saber numeros numero hay dato contiene como cadena java numbers bigdecimal string-parsing

solo - ¿Determinar si una cadena es un número y convertir en Java?



validar si un dato es string en java (15)

¿No está seguro de si cumple con todos sus requisitos, pero el código que se encuentra here podría orientarle en la dirección correcta?

Del artículo:

Para resumir, los pasos para un procesamiento de entrada adecuado son:

  1. Obtenga un NumberFormat apropiado y defina una variable ParsePosition.
  2. Establezca el índice ParsePosition en cero.
  3. Analice el valor de entrada con parse (String source, ParsePosition parsePosition).
  4. Realice operaciones de error si la longitud de entrada y el valor del índice ParsePosition no coinciden o si el Número analizado es nulo.
  5. De lo contrario, el valor pasa la validación.

Sé que las variantes de esta pregunta se han hecho con frecuencia antes (vea here y here por ejemplo), pero este no es un duplicado exacto de esas preguntas.

Me gustaría comprobar si un String es un número, y si es así, me gustaría guardarlo como un double . Hay varias maneras de hacer esto, pero todas ellas parecen inapropiadas para mis propósitos.

Una solución sería utilizar Double.parseDouble(s) o Double.parseDouble(s) similarmente new BigDecimal(s) . Sin embargo, esas soluciones no funcionan si hay comas presentes (por lo que "1,234" causaría una excepción). Por supuesto, podría eliminar todas las comas antes de usar estas técnicas, pero eso parecería plantear muchos problemas en otras configuraciones regionales.

Miré a Apache Commons NumberUtils.isNumber(s) , pero eso tiene el mismo problema de coma.

Consideré NumberFormat o DecimalFormat , pero parecían demasiado indulgentes. Por ejemplo, "1A" tiene el formato de "1" en lugar de indicar que no es un número. Además, algo como "127.0.0.1" se contará como el número 127 en lugar de indicar que no es un número.

Siento que mis requisitos no son tan exóticos que soy el primero en hacer esto, pero ninguna de las soluciones hace exactamente lo que necesito. Supongo que incluso no sé exactamente lo que necesito (de lo contrario podría escribir mi propio analizador), pero sé que las soluciones anteriores no funcionan por las razones indicadas. ¿Existe alguna solución o debo averiguar exactamente lo que necesito y escribir mi propio código para ello?


Creo que tiene un proceso de varios pasos para manejar aquí con una solución personalizada, si no está dispuesto a aceptar los resultados de DecimalFormat o las respuestas ya vinculadas.

1) Identificar los separadores decimales y de agrupación. Es posible que deba identificar otros símbolos de formato (como los indicadores de notación científica).

http://download.oracle.com/javase/1.4.2/docs/api/java/text/DecimalFormat.html#getDecimalFormatSymbols ()

2) Elimine todos los símbolos de agrupación (o cree una expresión regular, tenga cuidado con otros símbolos que acepte, como el decimal si lo hace). Luego tira el primer símbolo decimal. Otros símbolos según sea necesario.

3) Llamar a parse o isNumber .


Desafortunadamente, Double.parseDouble (s) o nuevo BigDecimal (s) parecen ser sus mejores opciones.

Usted cita las inquietudes de localización, pero desafortunadamente no hay forma de que todos los locales tengan una configuración confiable sin la especificación del usuario. Es simplemente imposible.

A veces se puede razonar sobre el esquema utilizado observando si las comas o los puntos se usan primero, si se usan ambos, pero esto no siempre es posible, así que ¿por qué intentarlo? Es mejor tener un sistema que sepa que funcione de manera confiable en ciertas situaciones que tratar de confiar en uno que puede funcionar en más situaciones pero también puede dar malos resultados ...

¿Qué representa el número 123,456? 123456 o 123.456?

Simplemente elimine las comas, espacios o puntos, según la configuración regional especificada por el usuario. Predeterminado para eliminar espacios y comas. Si desea hacerlo más estricto, solo elimine las comas O los espacios, no ambos, y solo antes del punto si hay uno. También debería ser bastante fácil de verificar manualmente si están espaciadas correctamente de tres en tres. De hecho, un analizador personalizado podría ser más fácil aquí.

Aquí hay un poco de una prueba de concepto. Es un poco (muy) desordenado, pero creo que funciona, y de todos modos se te ocurre la idea :).

public class StrictNumberParser { public double parse(String numberString) throws NumberFormatException { numberString = numberString.trim(); char[] numberChars = numberString.toCharArray(); Character separator = null; int separatorCount = 0; boolean noMoreSeparators = false; for (int index = 1; index < numberChars.length; index++) { char character = numberChars[index]; if (noMoreSeparators || separatorCount < 3) { if (character == ''.'') { if (separator != null) { throw new NumberFormatException(); } else { noMoreSeparators = true; } } else if (separator == null && (character == '','' || character == '' '')) { if (noMoreSeparators) { throw new NumberFormatException(); } separator = new Character(character); separatorCount = -1; } else if (!Character.isDigit(character)) { throw new NumberFormatException(); } separatorCount++; } else { if (character == ''.'') { noMoreSeparators = true; } else if (separator == null) { if (Character.isDigit(character)) { noMoreSeparators = true; } else if (character == '','' || character == '' '') { separator = new Character(character); } else { throw new NumberFormatException(); } } else if (!separator.equals(character)) { throw new NumberFormatException(); } separatorCount = 0; } } if (separator != null) { if (!noMoreSeparators && separatorCount != 3) { throw new NumberFormatException(); } numberString = numberString.replaceAll(separator.toString(), ""); } return Double.parseDouble(numberString); } public void testParse(String testString) { try { System.out.println("result: " + parse(testString)); } catch (NumberFormatException e) { System.out.println("Couldn''t parse number!"); } } public static void main(String[] args) { StrictNumberParser p = new StrictNumberParser(); p.testParse("123 45.6"); p.testParse("123 4567.8"); p.testParse("123 4567"); p.testParse("12 45"); p.testParse("123 456 45"); p.testParse("345.562,346"); p.testParse("123 456,789"); p.testParse("123,456,789"); p.testParse("123 456 789.52"); p.testParse("23,456,789"); p.testParse("3,456,789"); p.testParse("123 456.12"); p.testParse("1234567.8"); } }

EDITAR: obviamente esto debería extenderse para reconocer la notación científica, pero debería ser lo suficientemente simple, especialmente porque no tiene que validar nada después de la e, simplemente puede dejar que parseDouble falle si está mal formado.

También podría ser una buena idea extender adecuadamente NumberFormat con esto. tener un GetSeparator () para los números analizados y un setSeparator para dar el formato de salida deseado ... Este tipo de se encarga de la localización, pero nuevamente se debería hacer más trabajo para admitir '','' para decimales ...


Este código debe manejar la mayoría de las entradas, excepto las direcciones IP donde todos los grupos de dígitos están en tres (por ejemplo, 255.255.255.255 es válido, pero no 255.1.255.255). Tampoco soporta notación científica.

Funcionará con la mayoría de las variantes de separadores (",", "." O espacio). Si se detecta más de un separador, se asume que el primero es el separador de miles, con verificaciones adicionales (validez, etc.)

Editar: prevDigit se usa para verificar que el número use mil separadores correctamente. Si hay más de un grupo de miles, todos menos el primero deben estar en grupos de 3. Modifiqué el código para que quede más claro para que "3" no sea un número mágico sino una constante.

Edit 2: No me importan mucho los votos negativos, pero ¿puede alguien explicar cuál es el problema?

/* A number using thousand separator must have groups of 3 digits, except the first one. Numbers following the decimal separator can of course be unlimited. */ private final static int GROUP_SIZE=3; public static boolean isNumber(String input) { boolean inThousandSep = false; boolean inDecimalSep = false; boolean endsWithDigit = false; char thousandSep = ''/0''; int prevDigits = 0; for(int i=0; i < input.length(); i++) { char c = input.charAt(i); switch(c) { case '','': case ''.'': case '' '': endsWithDigit = false; if(inDecimalSep) return false; else if(inThousandSep) { if(c != thousandSep) inDecimalSep = true; if(prevDigits != GROUP_SIZE) return false; // Invalid use of separator } else { if(prevDigits > GROUP_SIZE || prevDigits == 0) return false; thousandSep = c; inThousandSep = true; } prevDigits = 0; break; default: if(Character.isDigit(c)) { prevDigits++; endsWithDigit = true; } else { return false; } } } return endsWithDigit; }

Código de prueba:

public static void main(String[] args) { System.out.println(isNumber("100")); // true System.out.println(isNumber("100.00")); // true System.out.println(isNumber("1,5")); // true System.out.println(isNumber("1,000,000.00.")); // false System.out.println(isNumber("100,00,2")); // false System.out.println(isNumber("123.123.23.123")); // false System.out.println(isNumber("123.123.123.123")); // true }


Este es un problema interesante. Pero tal vez sea un poco abierto? ¿Estás buscando específicamente identificar números de base 10, o hexadecimales, o qué? Estoy asumiendo base-10. ¿Qué pasa con la moneda? ¿Es eso importante? ¿O son solo números?

En cualquier caso, creo que puede utilizar las deficiencias del formato de Número a su favor. Dado que no es así como "1A", se interpretará como 1, ¿por qué no verifica el resultado formateando y comparando con la cadena original?

public static boolean isNumber(String s){ try{ Locale l = Locale.getDefault(); DecimalFormat df = new DecimalFormat("###.##;-##.##"); Number n = df.parse(s); String sb = df.format(n); return sb.equals(s); } catch(Exception e){ return false; } }

¿Qué piensas?


Esto es realmente interesante, y creo que la gente está tratando de complicarlo demasiado. Realmente solo descompondría esto por reglas:

1) Compruebe la notación científica (¿coincide con el patrón de ser todos los números, comas, puntos, - / + y tiene una ''e'' en ella?) - si es así, analice lo que desee

2) ¿Coincide con la expresión regular para caracteres numéricos válidos (0-9,. - +) (solo 1. - o + permitido) si es así, elimine todo lo que no sea un dígito y analícelo adecuadamente, de lo contrario fallará.

No puedo ver un atajo que funcione aquí, solo tome el enfoque de fuerza bruta, no todo en la programación puede ser (o debe ser) completamente elegante.


Esto tomará una cadena, contará sus decimales y comas, eliminará las comas, conservará un decimal válido (tenga en cuenta que esto se basa en la estandarización de los EE. UU., Para manejar 1.000.000,00 como 1 millón, este proceso tendría que tener el decimal y manejo de coma conmutada), determine si la estructura es válida y luego devuelva un doble. Devuelve nulo si la cadena no se pudo convertir. Edición : Añadido soporte para internacional o estadounidense. convertStoD (string, true) para EE. UU., convertStoD (string, falso) para no EE. UU. Los comentarios son ahora para la versión de Estados Unidos.

public double convertStoD(string s,bool isUS){ //string s = "some string or number, something dynamic"; bool isNegative = false; if(s.charAt(0)== ''-'') { s = s.subString(1); isNegative = true; } string ValidNumberArguements = new string(); if(isUS) { ValidNumberArguements = ",."; }else{ ValidNumberArguements = ".,"; } int length = s.length; int currentCommas = 0; int currentDecimals = 0; for(int i = 0; i < length; i++){ if(s.charAt(i) == ValidNumberArguements.charAt(0))//charAt(0) = , { currentCommas++; continue; } if(s.charAt(i) == ValidNumberArguements.charAt(1))//charAt(1) = . { currentDec++; continue; } if(s.charAt(i).matches("/D"))return null;//remove 1 A } if(currentDecimals > 1)return null;//remove 1.00.00 string decimalValue = ""; if(currentDecimals > 0) { int index = s.indexOf(ValidNumberArguements.charAt(1)); decimalValue += s.substring(index); s = s.substring(0,index); if(decimalValue.indexOf(ValidNumberArguements.charAt(0)) != -1)return null;//remove 1.00,000 } int allowedCommas = (s.length-1) / 3; if(currentCommas > allowedCommas)return null;//remove 10,00,000 String[] NumberParser = s.split(ValidNumberArguements.charAt(0)); length = NumberParser.length; StringBuilder returnString = new StringBuilder(); for(int i = 0; i < length; i++) { if(i == 0) { if(NumberParser[i].length > 3 && length > 1)return null;//remove 1234,0,000 returnString.append(NumberParser[i]); continue; } if(NumberParser[i].length != 3)return null;//ensure proper 1,000,000 returnString.append(NumberParser[i]); } returnString.append(decimalValue); double answer = Double.parseDouble(returnString); if(isNegative)answer *= -1; return answer; }


Lo mejor es hacerlo manualmente. Averigua lo que puedes aceptar como un número e ignora todo lo demás:

import java.lang.NumberFormatException; import java.util.regex.Pattern; import java.util.regex.Matcher; public class ParseDouble { public static void main(String[] argv) { String line = "$$$|%|#|1A|127.0.0.1|1,344|95|99.64"; for (String s : line.split("//|")) { try { System.out.println("parsed: " + any2double(s) ); }catch (NumberFormatException ne) { System.out.println(ne.getMessage()); } } } public static double any2double(String input) throws NumberFormatException { double out =0d; Pattern special = Pattern.compile("[^a-zA-Z0-9//.,]+"); Pattern letters = Pattern.compile("[a-zA-Z]+"); Pattern comma = Pattern.compile(","); Pattern allDigits = Pattern.compile("^[0-9]+$"); Pattern singleDouble = Pattern.compile("^[0-9]+//.[0-9]+$"); Matcher[] goodCases = new Matcher[]{ allDigits.matcher(input), singleDouble.matcher(input) }; Matcher[] nanCases = new Matcher[]{ special.matcher(input), letters.matcher(input) }; // maybe cases if (comma.matcher(input).find()){ out = Double.parseDouble( comma.matcher(input).replaceFirst(".")); return out; } for (Matcher m : nanCases) { if (m.find()) { throw new NumberFormatException("Bad input "+input); } } for (Matcher m : goodCases) { if (m.find()) { try { out = Double.parseDouble(input); return out; } catch (NumberFormatException ne){ System.out.println(ne.getMessage()); } } } throw new NumberFormatException("Could not parse "+input); } }


Mi entendimiento es que usted quiere cubrir los idiomas occidentales / latinos mientras retiene la interpretación más estricta posible. Entonces, lo que estoy haciendo aquí es pedirle a DecimalFormatSymbols que me diga qué son los separadores de agrupación, decimales, negativos y cero, y cambiarlos por símbolos que Double reconozca.

¿Cómo se realiza?

En los Estados Unidos, rechaza: "1A", "127.100.100.100" y acepta "1.47E-9"

En Alemania sigue rechazando "1A".

ACEPTA "1,024.00" pero lo interpreta correctamente como 1.024. Asimismo, acepta "127.100.100.100" como 127100100100.0

De hecho, la configuración regional alemana identifica y analiza correctamente "1,47E-9"

Déjame saber si tienes algún problema en un lugar diferente.

import java.util.Locale; import java.text.DecimalFormatSymbols; public class StrictNumberFormat { public static boolean isDouble(String s, Locale l) { String clean = convertLocaleCharacters(s,l); try { Double.valueOf(clean); return true; } catch (NumberFormatException nfe) { return false; } } public static double doubleValue(String s, Locale l) { return Double.valueOf(convertLocaleCharacters(s,l)); } public static boolean isDouble(String s) { return isDouble(s,Locale.getDefault()); } public static double doubleValue(String s) { return doubleValue(s,Locale.getDefault()); } private static String convertLocaleCharacters(String number, Locale l) { DecimalFormatSymbols symbols = new DecimalFormatSymbols(l); String grouping = getUnicodeRepresentation( symbols.getGroupingSeparator() ); String decimal = getUnicodeRepresentation( symbols.getDecimalSeparator() ); String negative = getUnicodeRepresentation( symbols.getMinusSign() ); String zero = getUnicodeRepresentation( symbols.getZeroDigit() ); String clean = number.replaceAll(grouping, ""); clean = clean.replaceAll(decimal, "."); clean = clean.replaceAll(negative, "-"); clean = clean.replaceAll(zero, "0"); return clean; } private static String getUnicodeRepresentation(char ch) { String unicodeString = Integer.toHexString(ch); //ch implicitly promoted to int while(unicodeString.length()<4) unicodeString = "0"+unicodeString; return "//u"+unicodeString; } }


Puede especificar la configuración regional que necesita:

NumberFormat nf = NumberFormat.getInstance(Locale.GERMAN); double myNumber = nf.parse(myString).doubleValue();

Esto debería funcionar en tu ejemplo, ya que la configuración regional alemana tiene comas como separador decimal.


Puede usar ParsePosition como una comprobación del consumo completo de la cadena en una operación NumberFormat.parse. Si la cadena se consume, entonces no tiene una situación "1A". Si no, lo haces y puedes comportarte en consecuencia. Vea here para un resumen rápido de la solución y here para ver el error JDK relacionado que está cerrado porque no se solucionará debido a la opción ParsePosition.


Si desea convertir un número de cadena que es decimal separado por comas a doble, puede usar DecimalSeparator + DecimalFormalSymbols:

final double strToDouble(String str, char separator){ DecimalFormatSymbols s = new DecimalFormatSymbols(); s.setDecimalSeparator(separator); DecimalFormat df = new DecimalFormat(); double num = 0; df.setDecimalFormatSymbols(s); try{ num = ((Double) df.parse(str)).doubleValue(); }catch(ClassCastException | ParseException ex){ // if you want, you could add something here to // indicate the string is not double } return num; }

Bueno, vamos a probarlo:

String a = "1.2"; String b = "2,3"; String c = "A1"; String d = "127.0.0.1"; System.out.println("/"" + a + "/" = " + strToDouble(a, '','')); System.out.println("/"" + a + "/" (with ''.'' as separator) = " + strToDouble(a, ''.'')); System.out.println("/"" + b + "/" = " + strToDouble(b, '','')); System.out.println("/"" + c + "/" = " + strToDouble(c, '','')); System.out.println("/"" + d + "/" = " + strToDouble(d, '',''));

Si ejecuta el código anterior, verá:

"1.2" = 0.0 "1.2" (with ''.'' as separator) = 1.2 "2,3" = 2.3 "A1" = 0.0 "127.0.0.1" = 0.0


Si establece la configuración regional correcta, la función incorporada en parseDouble funcionará con comas. El ejemplo está here .


Suena bastante raro, pero intentaría seguir esta respuesta y utilizar java.util.Scanner .

Scanner scanner = new Scanner(input); if (scanner.hasNextInt()) System.out.println(scanner.nextInt()); else if (scanner.hasNextDouble()) System.out.println(scanner.nextDouble()); else System.out.println("Not a number");

Para entradas como 1A , 127.0.0.1 , 1,234 , 6.02e-23 obtengo la siguiente salida:

Not a number Not a number 1234 6.02E-23

Scanner.useLocale se puede usar para cambiar la configuración regional deseada.


Uno de los replaceFirst fáciles sería usar replaceFirst for String que obtienes y verifica si es doble o no. En caso de que sea un doble - volver a convertir (si es necesario)