确定String是否为数字并使用Java进行转换?

我知道这个问题的变体之前经常被问到(例如这里和这里 ),但这并不是那些完全重复的。

我想检查String是否是一个数字,如果是,我想将它存储为double 。 有几种方法可以做到这一点,但所有这些方法似乎都不适合我的目的。

一种解决方案是使用Double.parseDouble(s)或类似的new BigDecimal(s) 。 但是,如果存在逗号,则这些解决方案不起作用(因此“1,234”会导致exception)。 在使用这些技术之前,我当然可以删除所有逗号,但这似乎会在其他语言环境中造成大量问题。

我查看了Apache Commons NumberUtils.isNumber(s) ,但是它遇到了相同的逗号问题。

我考虑过NumberFormatDecimalFormat ,但那些看起来太宽松了。 例如,“1A”被格式化为“1”而不是表示它不是数字。 此外,像“127.0.0.1”这样的东西将被计为数字127而不是表示它不是数字。

我觉得我的要求不是那么奇特,我是第一个做到这一点,但没有一个解决方案完全符合我的要求。 我想即使我不确切知道我需要什么(否则我可以编写自己的解析器),但我知道上述解决方案因指示的原因不起作用。 是否存在任何解决方案,或者我是否需要准确弄清楚我需要什么并为其编写自己的代码?

听起来很奇怪,但我会尝试按照这个答案并使用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"); 

对于1A127.0.0.1 6.02e-23输入,我得到以下输出:

 Not a number Not a number 1234 6.02E-23 

Scanner.useLocale可用于更改为所需的区域设置。

您可以指定所需的区域设置:

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

这应该适用于您的示例,因为德语区域设置将逗号作为小数点分隔符。

您可以使用ParsePosition检查NumberFormat.parse操作中字符串的完全消耗。 如果字符串被消耗,那么您没有“1A”情况。 如果没有,你可以并且可以相应地行事。 请参阅此处以获得解决方案的快速概述,以及由于ParsePosition选项而关闭的相关JDK错误。

不幸的是Double.parseDouble(s)或新的BigDecimal(s)似乎是你最好的选择。

您引用本地化问题,但遗憾的是,无论如何,用户无法可靠地支持所有区域设置。 这是不可能的。

有时您可以通过查看是否使用逗号或句点来判断所使用的方案,如果两者都被使用,但这并不总是可行的,那么为什么要尝试呢? 最好是让一个你知道的系统在某些情况下可靠地工作,而不是试图依赖一个可能在更多情况下工作但也会产生不良结果的系统……

123,456的数字代表什么? 123456或123.456?

只需删除逗号,空格或句点,具体取决于用户指定的区域设置。 默认为剥离空格和逗号。 如果你想使它更严格,只删除逗号或空格,而不是两者,只有在句号之前(如果有的话)。 如果它们在三个方向上间隔正确,也应该非常容易手动检查。 事实上,自定义解析器在这里可能是最简单的。

这是一个概念certificate。 它有点(非常)凌乱,但我认为它有效,你无论如何都得到了想法:)。

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

编辑:显然这需要扩展以识别科学记数法,但这应该足够简单,特别是因为你不必在e之后实际validation任何东西,你可以让parseDouble失败,如果它形成不良。

使用它正确扩展NumberFormat也可能是一个好主意。 有一个用于解析数字的getSeparator()和一个用于提供所需输出格式的setSeparator ......这种方式可以解决本地化问题,但是需要做更多的工作来支持','表示小数...

不确定它是否符合您的所有要求,但此处找到的代码可能会指向您正确的方向?

来自文章:

总而言之,正确输入处理的步骤如下:

  1. 获取适当的NumberFormat并定义ParsePosition变量。
  2. 将ParsePosition索引设置为零。
  3. 使用parse解析输入值(String source,ParsePosition parsePosition)。
  4. 如果输入长度和ParsePosition索引值不匹配或解析的Number为null,则执行错误操作。
  5. 否则,该值通过validation。

这是一个有趣的问题。 但也许它有点开放? 您是否专门寻找基数为10的数字,或hex,或者什么? 我假设基数为10。 货币怎么样? 这很重要吗? 或者只是数字。

无论如何,我认为您可以利用Number格式的缺陷来获得优势。 既然你没有像“1A”这样的东西,将被解释为1,为什么不通过格式化并与原始字符串进行比较来检查结果?

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

你怎么看?

这真的很有趣,我认为人们正试图过度复杂化它。 我真的只是按规则打破这个:

1)检查科学记数法(它是否符合所有数字,逗号,句号, – / +并且其中包含’e’的模式?) – 如果是,请解析您想要的

2)它是否与有效数字字符(0-9,。 – +)的正则表达式匹配(只有1。 – 或+允许),如果是这样,删除所有不是数字并正确解析的内容,否则失败。

我看不到这里可以使用的快捷方式,只需采用蛮力方法,而不是编程中的所有内容都可以(或者需要)完全优雅。

我的理解是,您希望涵盖西方/拉丁语言,同时保留尽可能严格的解释。 所以我在这里做的是要求DecimalFormatSymbols告诉我分组,十进制,负数和零分隔符是什么,并将它们交换为符号Double将识别。

它是如何表现的?

在美国,它拒绝:“1A”,“127.100.100.100”并接受“1.47E-9”

在德国,它仍拒绝“1A”

它接受“1,024.00”,但正确解释为1.024。 同样,它接受“127.100.100.100”为127100100100.0

事实上,德语区域设置正确识别并解析“1,47E-9”

如果您在其他语言环境中遇到任何问题,请与我们联系。

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

你最好手动做。 找出你可以接受的数字,忽略其他一切:

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

如果您将语言环境设置为正确,则内置parseDouble将使用逗号。 示例在这里 。

如果您不愿意接受DecimalFormat的结果或已经链接的答案,我认为您已经有了一个多步骤流程来处理自定义解决方案。

1)识别小数和分组分隔符。 您可能需要识别其他格式符号(例如科学记数法指示符)。

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

2)去掉所有分组符号(或制作一个正则表达式,注意你接受的其他符号,如果你这样做)。 然后去掉第一个小数符号。 其他符号根据需要。

3)调用parseisNumber

其中一个简单的方法就是使用replaceFirst for String得到并检查新的String是否为double。 万一它是双转换(如果需要)

如果要将逗号分隔的十进制的字符串数转换为double,可以使用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; } 

好吧,让我们测试一下:

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

如果你运行上面的代码,你会看到:

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

这将采用一个字符串,计算其小数和逗号,删除逗号,保存一个有效的十进制(请注意,这是基于美国标准化 – 为了处理1.000.000,00,因为这个过程必须有十进制和逗号处理切换),确定结构是否有效,然后返回一个double。 如果无法转换字符串,则返回null。 编辑 :添加对国际或美国的支持。 美国的convertStoD(字符串,true),非美国的convertStoD(字符串,false)。 现在评论为美国版。

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

此代码应处理大多数输入,但IP地址除外,其中所有数字组都是三位数(例如:255.255.255.255有效,但不是255.1.255.255)。 它也不支持科学记数法

它适用于大多数分隔符(“,”,“。”或空格)。 如果检测到多个分隔符,则假设第一个分隔符为千位分隔符,并附加检查(有效性等)

编辑: prevDigit用于检查该数字是否正确使用了千位分隔符。 如果有一组以上的数组,除了第一组之外的所有组都必须是3组。我修改了代码以使其更清晰,因此“3”不是幻数而是常数。

编辑2:我不介意下来投票多,但有人可以解释问题是什么吗?

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

测试代码:

 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 }