确定字符串是否为数字并在 Java 中进行转换?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5888987/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Determine if a String is a number and convert in Java?
提问by Michael McGowan
I know variants of this question have been asked frequently before (see hereand herefor instance), but this is notan exactduplicate of those.
我知道这个问题的变体以前经常被问到(例如,参见这里和这里),但这并不是这些问题的完全重复。
I would like to check if a String
is a number, and if so I would like to store it as a double
. There are several ways to do this, but all of them seem inappropriate for my purposes.
我想检查 aString
是否是数字,如果是,我想将其存储为double
. 有几种方法可以做到这一点,但所有这些似乎都不适合我的目的。
One solution would be to use Double.parseDouble(s)
or similarly new BigDecimal(s)
. However, those solutions don't work if there are commas present (so "1,234" would cause an exception). I could of course strip out all commas before using these techniques, but that would seem to pose loads of problems in other locales.
一种解决方案是使用Double.parseDouble(s)
或类似地new BigDecimal(s)
。但是,如果存在逗号,则这些解决方案不起作用(因此“1,234”会导致异常)。我当然可以在使用这些技术之前去掉所有的逗号,但这在其他语言环境中似乎会带来很多问题。
I looked at Apache Commons NumberUtils.isNumber(s)
, but that suffers from the same comma issue.
我查看了 Apache Commons NumberUtils.isNumber(s)
,但它遇到了相同的逗号问题。
I considered NumberFormat
or DecimalFormat
, but those seemed far too lenient. For instance, "1A" is formatted to "1" instead of indicating that it's not a number. Furthermore, something like "127.0.0.1" will be counted as the number 127 instead of indicating that it's not a number.
我考虑过NumberFormat
或DecimalFormat
,但那些似乎太宽容了。例如,“1A”被格式化为“1”而不是表示它不是数字。此外,诸如“127.0.0.1”之类的内容将被视为数字 127,而不是表示它不是数字。
I feel like my requirements aren't so exotic that I'm the first to do this, but none of the solutions does exactly what I need. I suppose even I don't know exactlywhat I need (otherwise I could write my own parser), but I know the above solutions do not work for the reasons indicated. Does any solution exist, or do I need to figure out precisely what I need and write my own code for it?
我觉得我的要求并不是那么奇特,以至于我是第一个这样做的人,但是没有一个解决方案能完全满足我的需要。我想即使我也不确切知道我需要什么(否则我可以编写自己的解析器),但我知道由于上述原因,上述解决方案不起作用。是否存在任何解决方案,或者我是否需要准确地弄清楚我需要什么并为其编写自己的代码?
采纳答案by Giulio Piancastelli
Sounds quite weird, but I would try to follow this answerand use java.util.Scanner
.
听起来很奇怪,但我会尝试按照这个答案并使用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");
For inputs such as 1A
, 127.0.0.1
, 1,234
, 6.02e-23
I get the following output:
对于诸如1A
, 127.0.0.1
, 之类的输入1,234
,6.02e-23
我得到以下输出:
Not a number
Not a number
1234
6.02E-23
Scanner.useLocale
can be used to change to the desired locale.
Scanner.useLocale
可用于更改为所需的语言环境。
回答by adranale
You can specify the Locale that you need:
您可以指定所需的语言环境:
NumberFormat nf = NumberFormat.getInstance(Locale.GERMAN);
double myNumber = nf.parse(myString).doubleValue();
This should work in your example since German Locale has commas as decimal separator.
这应该适用于您的示例,因为德国语言环境使用逗号作为小数点分隔符。
回答by philwb
You can use the ParsePosition as a check for complete consumption of the string in a NumberFormat.parse operation. If the string is consumed, then you don't have a "1A" situation. If not, you do and can behave accordingly. See herefor a quick outline of the solution and herefor the related JDK bug that is closed as wont fix because of the ParsePosition option.
您可以使用 ParsePosition 作为检查是否在 NumberFormat.parse 操作中完全消耗字符串。如果字符串被消耗,那么您就没有“1A”的情况。如果没有,您可以并且可以相应地行事。见这里的解决方案的快速大纲和这里的相关JDK错误,被关闭的修复不会因为一个ParsePosition选项。
回答by Peter Svensson
Not sure if it meets all your requirements, but the code found heremight point you in the right direction?
不确定它是否满足您的所有要求,但此处找到的代码可能会为您指明正确的方向?
From the article:
从文章:
To summarize, the steps for proper input processing are:
- Get an appropriate NumberFormat and define a ParsePosition variable.
- Set the ParsePosition index to zero.
- Parse the input value with parse(String source, ParsePosition parsePosition).
- Perform error operations if the input length and ParsePosition index value don't match or if the parsed Number is null.
- Otherwise, the value passed validation.
总而言之,正确处理输入的步骤是:
- 获取适当的 NumberFormat 并定义 ParsePosition 变量。
- 将 ParsePosition 索引设置为零。
- 使用 parse(String source, ParsePosition parsePosition) 解析输入值。
- 如果输入长度和 ParsePosition 索引值不匹配或解析的 Number 为空,则执行错误操作。
- 否则,该值通过验证。
回答by Elias Vasylenko
Unfortunately Double.parseDouble(s) or new BigDecimal(s) seem to be your best options.
不幸的是 Double.parseDouble(s) 或 new BigDecimal(s) 似乎是你最好的选择。
You cite localisation concerns, but unfortunately there is no way reliably support all locales w/o specification by the user anyway. It is just impossible.
您引用了本地化问题,但不幸的是,无论如何都没有办法可靠地支持所有没有用户规范的语言环境。这是不可能的。
Sometimes you can reason about the scheme used by looking at whether commas or periods are used first, if both are used, but this isn't always possible, so why even try? Better to have a system which you know works reliably in certain situations than try to rely on one which may work in more situations but can also give bad results...
有时您可以通过查看是先使用逗号还是句点(如果两者都使用)来推断所使用的方案,但这并不总是可行的,那为什么还要尝试呢?最好拥有一个您知道在某些情况下可以可靠运行的系统,而不是尝试依赖一个可以在更多情况下工作但也可能产生糟糕结果的系统......
What does the number 123,456 represent? 123456 or 123.456?
数字 123,456 代表什么?123456 还是 123.456?
Just strip commas, or spaces, or periods, depending on locale specified by user. Default to stripping spaces and commas. If you want to make it stricter, only strip commas OR spaces, not both, and only before the period if there is one. Also should be pretty easy to check manually if they are spaced properly in threes. In fact a custom parser might be easiest here.
根据用户指定的区域设置,只需去除逗号、空格或句点即可。默认为去除空格和逗号。如果你想让它更严格,只去掉逗号或空格,而不是两者,如果有的话,只在句点之前。如果它们在三个中正确间隔,也应该很容易手动检查。事实上,自定义解析器在这里可能是最简单的。
Here is a bit of a proof of concept. It's a bit (very) messy but I reckon it works, and you get the idea anyways :).
这是一个概念证明。它有点(非常)凌乱,但我认为它有效,无论如何你都会明白:)。
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");
}
}
EDIT: obviously this would need to be extended for recognising scientific notation, but this should be simple enough, especially as you don't have to actually validate anything after the e, you can just let parseDouble fail if it is badly formed.
编辑:显然这需要扩展以识别科学记数法,但这应该足够简单,特别是因为您不必在 e 之后实际验证任何内容,如果格式错误,您可以让 parseDouble 失败。
Also might be a good idea to properly extend NumberFormat with this. have a getSeparator() for parsed numbers and a setSeparator for giving desired output format... This sort of takes care of localisation, but again more work would need to be done to support ',' for decimals...
用这个正确扩展 NumberFormat 也可能是一个好主意。有一个用于解析数字的 getSeparator() 和一个用于提供所需输出格式的 setSeparator ......这种负责本地化,但同样需要做更多的工作来支持小数的“,”......
回答by jharig23
This is an interesting problem. But perhaps it is a little open-ended? Are you looking specifically to identify base-10 numbers, or hex, or what? I'm assuming base-10. What about currency? Is that important? Or is it just numbers.
这是一个有趣的问题。但也许它有点开放?您是专门想识别基数为 10 的数字还是十六进制,还是什么?我假设基数为 10。货币呢?那很重要吗?或者它只是数字。
In any case, I think that you can use the deficiencies of Number format to your advantage. Since you no that something like "1A", will be interpreted as 1, why not check the result by formatting it and comparing against the original string?
无论如何,我认为您可以利用 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;
}
}
What do you think?
你怎么认为?
回答by Rick Mangi
This is really interesting, and I think people are trying to overcomplicate it. I would really just break this down by rules:
这真的很有趣,我认为人们试图将其复杂化。我真的只是按规则分解:
1) Check for scientific notation (does it match the pattern of being all numbers, commas, periods, -/+ and having an 'e' in it?) -- if so, parse however you want
1) 检查科学记数法(它是否与所有数字、逗号、句点、-/+ 以及其中包含“e”的模式相匹配?)--如果是,请根据需要进行解析
2) Does it match the regexp for valid numeric characters (0-9 , . - +) (only 1 . - or + allowed) if so, strip out everything that's not a digit and parse appropriately, otherwise fail.
2) 它是否与有效数字字符 (0-9 , . - +) 的正则表达式匹配(仅允许 1 . - 或 + ),如果是,则去掉所有不是数字的内容并适当解析,否则失败。
I can't see a shortcut that's going to work here, just take the brute force approach, not everything in programming can be (or needs to be) completely elegant.
我看不到可以在这里工作的捷径,只是采取蛮力的方法,并不是编程中的一切都可以(或需要)完全优雅。
回答by Falkreon
My understanding is that you want to cover Western/Latin languages while retaining as much strict interpretation as possible. So what I'm doing here is asking DecimalFormatSymbols to tell me what the grouping, decimal, negative, and zero separators are, and swapping them out for symbols Double will recognize.
我的理解是,您希望涵盖西方/拉丁语言,同时尽可能保留严格的解释。所以我在这里做的是要求 DecimalFormatSymbols 告诉我分组、小数、负数和零分隔符是什么,并将它们交换为 Double 将识别的符号。
How does it perform?
它的表现如何?
In the US, it rejects: "1A", "127.100.100.100" and accepts "1.47E-9"
在美国,它拒绝:“1A”、“127.100.100.100”并接受“1.47E-9”
In Germany it still rejects "1A"
在德国它仍然拒绝“1A”
It ACCEPTS "1,024.00" but interprets it correctly as 1.024. Likewise, it accepts "127.100.100.100" as 127100100100.0
它接受“1,024.00”,但将其正确解释为 1.024。同样,它接受“127.100.100.100”作为 127100100100.0
In fact, the German locale correctly identifies and parses "1,47E-9"
事实上,德语语言环境正确识别和解析“1,47E-9”
Let me know if you have any trouble in a different locale.
如果您在不同的语言环境中遇到任何问题,请告诉我。
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;
}
}
回答by Ярослав Рахматуллин
You're best off doing it manually. Figure out what you can accept as a number and disregard everything else:
你最好手动完成。找出您可以接受的数字并忽略其他所有内容:
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);
}
}
回答by Travis J
This will take a string, count its decimals and commas, remove commas, conserve a valid decimal (note that this is based on US standardization - in order to handle 1.000.000,00 as 1 million this process would have to have the decimal and comma handling switched), determine if the structure is valid, and then return a double. Returns null if the string could not be converted. Edit: Added support for international or US. convertStoD(string,true) for US, convertStoD(string,false) for non US. Comments are now for US version.
这将需要一个字符串,计算它的小数点和逗号,删除逗号,保留一个有效的小数点(请注意,这是基于美国标准化 - 为了将 1.000.000,00 处理为 100 万,这个过程必须有小数点和逗号处理切换),确定结构是否有效,然后返回双精度值。如果无法转换字符串,则返回 null。编辑:增加了对国际或美国的支持。美国的 convertStoD(string,true),非美国的 convertStoD(string,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;
}