java - resultado - obtener int de un jtextfield
Cómo saber si el valor contenido en una cadena es doble o no (14)
El uso de un Scanner
será significativamente más lento que con Double.parseDouble(String s)
.
private static Random rand = new Random();
private static final String regExp = "[//x00-//x20]*[+-]?(((((//p{Digit}+)(//.)?((//p{Digit}+)?)([eE][+-]?(//p{Digit}+))?)|(//.((//p{Digit}+))([eE][+-]?(//p{Digit}+))?)|(((0[xX](//p{XDigit}+)(//.)?)|(0[xX](//p{XDigit}+)?(//.)(//p{XDigit}+)))[pP][+-]?(//p{Digit}+)))[fFdD]?))[//x00-//x20]*";
private static final Pattern pattern = Pattern.compile(regExp);
public static void main(String[] args) {
int trials = 50000;
String[] values = new String[trials];
// initialize the array
// about half the values will be parsable as double
for( int i = 0; i < trials; ++i ) {
double d = rand.nextDouble();
boolean b = rand.nextBoolean();
values[i] = (b ? "" : "abc") + d;
}
long start = System.currentTimeMillis();
int parseCount = 0;
for( int i = 0; i < trials; ++i ) {
if( isDoubleParse(values[i]) ) {
parseCount++;
}
}
long end = System.currentTimeMillis();
long elapsed = end - start;
System.out.println("Elapsed time parsing: " + elapsed + " ms");
System.out.println("Doubles: " + parseCount);
// reset the timer for the next run
start = System.currentTimeMillis();
int scanCount = 0;
for( int i = 0; i < trials; ++i ) {
if( isDoubleScan(values[i]) ) {
scanCount++;
}
}
end = System.currentTimeMillis();
elapsed = end - start;
System.out.println("Elapsed time scanning: " + elapsed + " ms");
System.out.println("Doubles: " + scanCount);
// reset the timer for the next run
start = System.currentTimeMillis();
int regexCount = 0;
for( int i = 0; i < trials; ++i ) {
if( isDoubleRegex(values[i]) ) {
regexCount++;
}
}
end = System.currentTimeMillis();
elapsed = end - start;
System.out.println("Elapsed time regex (naive): " + elapsed + " ms");
System.out.println("Doubles: " + naiveRegexCount);
// reset the timer for the next run
start = System.currentTimeMillis();
int compiledRegexCount = 0;
for( int i = 0; i < trials; ++i ) {
if( isDoubleCompiledRegex(values[i]) ) {
compiledRegexCount++;
}
}
end = System.currentTimeMillis();
elapsed = end - start;
System.out.println("Elapsed time regex (compiled): " + elapsed + " ms");
System.out.println("Doubles: " + compiledRegexCount);
}
public static boolean isDoubleParse(String s) {
if( s == null ) return false;
try {
Double.parseDouble(s);
return true;
} catch (NumberFormatException e) {
return false;
}
}
public static boolean isDoubleScan(String s) {
Scanner scanner = new Scanner(s);
return scanner.hasNextDouble();
}
public static boolean isDoubleRegex(String s) {
return s.matches(regExp);
}
public static boolean isDoubleCompiledRegex(String s) {
Matcher m = pattern.matcher(s);
return m.matches();
}
Cuando ejecuto el código anterior obtengo el siguiente resultado:
Análisis del tiempo transcurrido: 235 ms
Dobles: 24966
Escaneo del tiempo transcurrido: 31358 ms
Dobles: 24966
Tiempo transcurrido Regex (ingenuo): 1829 ms
Dobles: 24966
Tiempo transcurrido Regex (compilado): 109 ms
Dobles: 24966
El método de expresión regular se ejecuta bastante rápido dada la complejidad de la expresión regular, pero aún no es tan rápido como simplemente analizar mediante Double.parseDouble(s)
. Como se señala en los comentarios, hay algunos valores como NaN
que pasan el analizador que probablemente no deberían.
Actualizar:
Precompilar la expresión regular como lo sugiere @ Gabe hace toda la diferencia. El método de expresión regular compilado es ahora el claro ganador.
En Java, estoy tratando de averiguar si el valor contenido en una cadena es doble o no.
Hay una nota sobre esto en la fuente de Double
:
[...] Para evitar llamar a este método en una cadena no válida y tener una
NumberFormatException
, la siguiente expresión regular se puede utilizar para seleccionar la cadena de entrada: [...]
La forma final de las expresiones regulares que sigue es bastante larga:
[/x00-/x20]*[+-]?(NaN|Infinity|((((/p{Digit}+)(/.)?((/p{Digit}+)?)([eE][+-]?(/p{Digit}+))?)|(/.((/p{Digit}+))([eE][+-]?(/p{Digit}+))?)|(((0[xX](/p{XDigit}+)(/.)?)|(0[xX](/p{XDigit}+)?(/.)(/p{XDigit}+)))[pP][+-]?(/p{Digit}+)))[fFdD]?))[/x00-/x20]*
Con este método, sin embargo, puede excluir fácilmente algunos dobles especiales como Infinity
y NaN
que son aceptados por Double.parseDouble
. Por ejemplo, así:
String regExp = "[//x00-//x20]*[+-]?(((((//p{Digit}+)(//.)?((//p{Digit}+)?)([eE][+-]?(//p{Digit}+))?)|(//.((//p{Digit}+))([eE][+-]?(//p{Digit}+))?)|(((0[xX](//p{XDigit}+)(//.)?)|(0[xX](//p{XDigit}+)?(//.)(//p{XDigit}+)))[pP][+-]?(//p{Digit}+)))[fFdD]?))[//x00-//x20]*";
boolean matches = yourString.matches(regExp);
Modifiqué el método isInteger () de Jonas para obtener el método isDecimal () para mi propio proyecto y estoy enumerando los códigos a continuación.
Puede ser que alguien pueda cambiar agregar más códigos para distinguir entre un doble y un flotador.
Debería salir bastante rápido y probablemente mejor que la expresión regular, aunque no lo he comprobado. El único defecto es que se comporta mal en caso de una condición de desbordamiento, etc.
Tenga en cuenta que hago referencia a la publicación de Bill en ¿Cuál es la mejor manera de verificar si una cadena representa un número entero en Java?
public boolean isDecimal(String str) {
if (str == null) {
return false;
}
int length = str.length();
if (length == 1 ) {
return false;
}
int i = 0;
if (str.charAt(0) == ''-'') {
if (length < 3) {
return false;
}
i = 1;
}
int numOfDot = 0;
for (; i < length; i++) {
char c = str.charAt(i);
if (c == ''.'')
numOfDot++;
else if (c == ''/'')
return false;
else if (c < ''.'' || c > ''9'') {
return false;
}
}
if (numOfDot != 1 )
return false;
return true;
}
Otros han especulado que es posible que desee saber también que la entrada NO se expresa como un número entero. Dependiendo de sus requisitos, esto puede hacer el trabajo rápido y sucio:
public static void main(String[] args) throws Exception {
System.out.println(isNonIntegerDouble("12")); //false
System.out.println(isNonIntegerDouble("12.1")); //true
System.out.println(isNonIntegerDouble("12.0")); //true
}
public static boolean isNonIntegerDouble(String in) {
try {
Double.parseDouble(in);
} catch (NumberFormatException nfe) {
return false;
}
try {
new BigInteger(in);
} catch (NumberFormatException nfe) {
return true;
}
return false;
}
En este punto, sin embargo, creo que la coincidencia de cuerdas sería una elección más adecuada.
Podría intentar analizarlo con Double.parseDouble(String s)
Esto devolverá el doble si el análisis fue exitoso y una excepción si no es analizable.
De modo que podría envolver todo en una función que contenga un try-catch, y devolver false si obtuvo una excepción o verdadero si obtuvo un valor real.
Podría intentar igualarlo con una expresión regular, como se describe aquí: http://www.regular-expressions.info/floatingpoint.html
Preguntas relacionadas:
Regex para analizar los números de coma flotante internacionales Cómo detectar un número de coma flotante usando una expresión regular
Puede crear un Scanner(String)
y usar el método hasNextDouble()
. De su javadoc:
Devuelve
true
si el próximo token en la entrada de este escáner se puede interpretar como un valor doble usando el métodonextDouble()
. El escáner no avanza más allá de cualquier entrada.
Por ejemplo, este fragmento:
List<String> values = Arrays.asList("foo", "1", "2.3", "1f", "0.2d", "3.14");
for (String source : values) {
Scanner scanner = new Scanner(source);
System.out.println(String.format("%4s: %s", source, scanner.hasNextDouble()));
}
Produciría el siguiente resultado:
foo: false 1: true 2.3: true 1f: false 0.2d: false 3.14: true
Puede usar la siguiente expresión regular en la cadena:
[-+]?[0-9]*/.?[0-9]*
y ver si coincide
Puede utilizar la clase util de Apache Commons Lang:
NumberUtils.isNumber(aString);
No es seguro y no requiere el uso de un bloque try-catch.
Nota: para analizar dobles, funciona si el separador decimal es un punto .
Editar: isNumber está en desuso y se eliminará de Lang 4.0
Mejor usar:
NumberUtils.isCreatable(aString);
Si pudiera encontrar una forma de aislar el número de la cadena, quizás usando el método de división. Y digamos num[1] = 25;
entonces podrías hacer algo como esto para verificar si es un doble.
boolean isDouble;
if(num[1].contains(".")){
isDouble = true;
}
else{
isDouble = false;
}
Sugeriría esto:
try {
d = Double.parseDouble(myString);
}
catch (NumberFormatException ex) {
// Do something smart here...
}
boolean isDouble(String str) {
try {
Double.parseDouble(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
public boolean isDouble( String input )
{
try
{
Double.parseDouble( input );
return true;
}
catch( Exception e)
{
return false;
}
}
public boolean isDouble(String value) {
try {
Double.parseDouble(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}