Самый быстрый способ проверить, может ли String быть разобран на Double in Java
Я знаю там миллион способов сделать это, но что самое быстрое? Это должно включать научную нотацию.
ПРИМЕЧАНИЕ. Меня не интересует преобразование значения в Double, мне интересно только знать, возможно ли это. т.е. private boolean isDouble(String value)
.
Ответы
Ответ 1
Вы можете проверить его, используя то же регулярное выражение, которое использует Double-класс. Это хорошо описано здесь:
http://docs.oracle.com/javase/6/docs/api/java/lang/Double.html#valueOf%28java.lang.String%29
Вот часть кода:
Чтобы избежать вызова этого метода на недопустимой строке и сбрасывать исключение NumberFormatException, регулярное выражение, приведенное ниже, можно использовать для отображения входной строки:
final String Digits = "(\\p{Digit}+)";
final String HexDigits = "(\\p{XDigit}+)";
// an exponent is 'e' or 'E' followed by an optionally
// signed decimal integer.
final String Exp = "[eE][+-]?"+Digits;
final String fpRegex =
("[\\x00-\\x20]*"+ // Optional leading "whitespace"
"[+-]?(" + // Optional sign character
"NaN|" + // "NaN" string
"Infinity|" + // "Infinity" string
// A decimal floating-point string representing a finite positive
// number without a leading sign has at most five basic pieces:
// Digits . Digits ExponentPart FloatTypeSuffix
//
// Since this method allows integer-only strings as input
// in addition to strings of floating-point literals, the
// two sub-patterns below are simplifications of the grammar
// productions from the Java Language Specification, 2nd
// edition, section 3.10.2.
// Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
"((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+
// . Digits ExponentPart_opt FloatTypeSuffix_opt
"(\\.("+Digits+")("+Exp+")?)|"+
// Hexadecimal strings
"((" +
// 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "(\\.)?)|" +
// 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
")[pP][+-]?" + Digits + "))" +
"[fFdD]?))" +
"[\\x00-\\x20]*");// Optional trailing "whitespace"
if (Pattern.matches(fpRegex, myString))
Double.valueOf(myString); // Will not throw NumberFormatException
else {
// Perform suitable alternative action
}
Ответ 2
В a > . Это немного странно:
Допустимые числа включают шестнадцатеричные, отмеченные с помощью классификатора 0x, научной нотации и числа, помеченные квалификатором типа (например, 123L).
но я предполагаю, что это может быть быстрее, чем регулярные выражения, или бросать и ловить исключение.
Ответ 3
Apache Commons NumberUtil на самом деле довольно быстро. Я предполагаю, что это быстрее, чем любой
regexp.
Ответ 4
Я использую следующий код, чтобы проверить, может ли строка быть разобрана в double:
public static boolean isDouble(String str) {
if (str == null) {
return false;
}
int length = str.length();
if (length == 0) {
return false;
}
int i = 0;
if (str.charAt(0) == '-') {
if (length == 1) {
return false;
}
++i;
}
int integerPartSize = 0;
int exponentPartSize = -1;
while (i < length) {
char c = str.charAt(i);
if (c < '0' || c > '9') {
if (c == '.' && integerPartSize > 0 && exponentPartSize == -1) {
exponentPartSize = 0;
} else {
return false;
}
} else if (exponentPartSize > -1) {
++exponentPartSize;
} else {
++integerPartSize;
}
++i;
}
if ((str.charAt(0) == '0' && i > 1 && exponentPartSize < 1)
|| exponentPartSize == 0 || (str.charAt(length - 1) == '.')) {
return false;
}
return true;
}
Мне известно, что результат не совсем то же самое, что и для обычного выражения в классе Double, но этот метод выполняется намного быстрее, и результат достаточно хорош для моих нужд. Это мои модульные тесты для метода.
@Test
public void shouldReturnTrueIfStringIsDouble() {
assertThat(Utils.isDouble("0.0")).isTrue();
assertThat(Utils.isDouble("0.1")).isTrue();
assertThat(Utils.isDouble("-0.0")).isTrue();
assertThat(Utils.isDouble("-0.1")).isTrue();
assertThat(Utils.isDouble("1.0067890")).isTrue();
assertThat(Utils.isDouble("0")).isTrue();
assertThat(Utils.isDouble("1")).isTrue();
}
@Test
public void shouldReturnFalseIfStringIsNotDouble() {
assertThat(Utils.isDouble(".01")).isFalse();
assertThat(Utils.isDouble("0.1f")).isFalse();
assertThat(Utils.isDouble("a")).isFalse();
assertThat(Utils.isDouble("-")).isFalse();
assertThat(Utils.isDouble("-1.")).isFalse();
assertThat(Utils.isDouble("-.1")).isFalse();
assertThat(Utils.isDouble("123.")).isFalse();
assertThat(Utils.isDouble("1.2.3")).isFalse();
assertThat(Utils.isDouble("1,3")).isFalse();
}
Ответ 5
Я думаю, что попытка конвертировать его в двойную, и перехват исключения будет самым быстрым способом проверить... другой способ, о котором я могу думать, - расщепить строку на период ('.'), а затем проверить, что каждая часть массива split содержит только целые числа... но я думаю, что первый способ был бы быстрее
Ответ 6
Я попытался использовать блок кода и, похоже, быстрее выбрал исключение
String a = "123f15512551";
System.out.println(System.currentTimeMillis());
a.matches("^\\d+\\.\\d+$");
System.out.println(System.currentTimeMillis());
try{
Double.valueOf(a);
}catch(Exception e){
System.out.println(System.currentTimeMillis());
}
Вывод:
1324316024735
1324316024737
1324316024737
Ответ 7
Исключения не должны использоваться для управления потоком, хотя авторы Java затруднили использование NumberFormatException
таким образом.
Класс java.util.Scanner
имеет метод hasNextDouble
, чтобы проверить, можно ли считать String
как double.
Под капотом Scanner
используются регулярные выражения (через предварительно скомпилированные шаблоны), чтобы определить, можно ли преобразовать String
в число целых чисел или чисел с плавающей запятой. Шаблоны скомпилированы в методе buildFloatAndDecimalPattern
, который вы можете просмотреть в GrepCode здесь.
Предварительно скомпилированный шаблон имеет дополнительное преимущество: он быстрее, чем использование блока try/catch.
Здесь метод, упомянутый выше, в случае, если GrepCode исчезает в один прекрасный день:
private void buildFloatAndDecimalPattern() {
// \\p{javaDigit} may not be perfect, see above
String digit = "([0-9]|(\\p{javaDigit}))";
String exponent = "([eE][+-]?"+digit+"+)?";
String groupedNumeral = "("+non0Digit+digit+"?"+digit+"?("+
groupSeparator+digit+digit+digit+")+)";
// Once again digit++ is used for performance, as above
String numeral = "(("+digit+"++)|"+groupedNumeral+")";
String decimalNumeral = "("+numeral+"|"+numeral +
decimalSeparator + digit + "*+|"+ decimalSeparator +
digit + "++)";
String nonNumber = "(NaN|"+nanString+"|Infinity|"+
infinityString+")";
String positiveFloat = "(" + positivePrefix + decimalNumeral +
positiveSuffix + exponent + ")";
String negativeFloat = "(" + negativePrefix + decimalNumeral +
negativeSuffix + exponent + ")";
String decimal = "(([-+]?" + decimalNumeral + exponent + ")|"+
positiveFloat + "|" + negativeFloat + ")";
String hexFloat =
"[-+]?0[xX][0-9a-fA-F]*\\.[0-9a-fA-F]+([pP][-+]?[0-9]+)?";
String positiveNonNumber = "(" + positivePrefix + nonNumber +
positiveSuffix + ")";
String negativeNonNumber = "(" + negativePrefix + nonNumber +
negativeSuffix + ")";
String signedNonNumber = "(([-+]?"+nonNumber+")|" +
positiveNonNumber + "|" +
negativeNonNumber + ")";
floatPattern = Pattern.compile(decimal + "|" + hexFloat + "|" +
signedNonNumber);
decimalPattern = Pattern.compile(decimal);
}