Java 解析字符串中所有双精度值的函数

Java function to parse all doubles from string

我知道有人问过这个问题 before¹ 但回复似乎并未涵盖所有极端情况。

我尝试用测试用例实施建议¹

String("Doubles -1.0, 0, 1, 1.12345 and 2.50")

哪个应该return

[-1, 0, 1, 1.12345, 2.50]:

import java.util.Scanner;
import java.util.ArrayList;
import java.util.Locale;
public class Main
{
    public static void main(String[] args) {
        String string = new String("Doubles -1.0, 0, 1, 1.12345 and 2.50");
        System.out.println(string);
        ArrayList<Double> doubles = getDoublesFromString(string);
        System.out.println(doubles);
    }
    
    public static ArrayList<Double> getDoublesFromString(String string){
        Scanner parser = new Scanner(string);
        parser.useLocale(Locale.US);
        ArrayList<Double> doubles = new ArrayList<Double>();
        double currentDouble;
        while (parser.hasNext()){
            if(parser.hasNextDouble()){
                currentDouble = parser.nextDouble();
                doubles.add(currentDouble);
            }
            else {
                parser.next();
            }
        }
        parser.close();
        return doubles;
    }
}

上面的代码 returns [1.12345, 2.5].

我是不是执行错了?捕捉负数和 0 的修复方法是什么?

我会在这里使用正则表达式查找所有方法:

String string = new String("Doubles -1.0, 0, 1, 1.12345 and 2.50");
List<String> nums = new ArrayList<>();

String pattern = "-?\d+(?:\.\d+)?";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(string);

while (m.find()) {
    nums.add(m.group());
}

System.out.println(nums);  // [-1.0, 0, 1, 1.12345, 2.50]

顺便说一句,您的问题使用了 String 构造函数,它很少被使用,但很有趣,尤其是对于我们这些从未使用过它的人来说。

下面是对正则表达式模式的解释:

-?            match an optional leading negative sign
\d+          match a whole number
(?:\.\d+)?  match an optional decimal component

首先:我也会使用正则表达式解决方案......它更好,以下只是使用 splitreplace/ 的替代方案replaceAll 在捕捉 Exception 时:

public static void main(String[] args) {
    // input
    String s = "Doubles -1.0, 0, 1, 1.12345 and 2.50";
    // split by whitespace(s) (keep in mind the commas will stay)
    String[] parts = s.split("\s+");
    // create a collection to store the Doubles
    List<Double> nums = new ArrayList<>();
    // stream the result of the split operation and
    Arrays.stream(parts).forEach(p -> {
        // try to…
        try {
            // replace all commas and parse the value
            nums.add(Double.parseDouble(p.replaceAll(",", "")));
        } catch (Exception e) {
            // which won't work for words like "Doubles", so print an error on those
            System.err.println("Could not parse \"" + p + "\"");
        }
    });
    // finally print all successfully parsed Double values
    nums.forEach(System.out::println);
}

输出:

Could not parse "Doubles"
Could not parse "and"
-1.0
0.0
1.0
1.12345
2.5

对于您的具体示例,在扫描器的构造中添加它就足够了:parser.useDelimiter("\s|,");

您的代码中的问题是包含逗号的标记未被识别为有效的双精度值。上面的代码所做的是将扫描器配置为不仅考虑空白字符而且考虑逗号作为标记分隔符,因此逗号将不再出现在标记中,因此它将是一个有效的双精度字符,将被成功解析。

我认为这是最合适的解决方案,因为匹配所有双打实际上很复杂。下面,我粘贴了 Scanner 用来执行此操作的正则表达式,看看这到底有多复杂。与拆分字符串然后使用 Double.parseDouble 相比,这非常相似但涉及的自定义代码更少,更重要的是没有异常抛出,这很慢。

(([-+]?((((([0-9\p{javaDigit}]))++)|(\p{javaDigit}&&[^0]?(([0-9\p{javaDigit}]))?(\x{2c}(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}])))+))|(((([0-9\p{javaDigit}]))++)|(\p{javaDigit}&&[^0]?(([0-9\p{javaDigit}]))?(\x{2c}(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}])))+))\x{2e}(([0-9\p{javaDigit}]))+|\x{2e}(([0-9\p{javaDigit}]))++)([eE][+-]?(([0-9\p{javaDigit}]))+)?)|(((((([0-9\p{javaDigit}]))++)|(\p{javaDigit}&&[^0]?(([0-9\p{javaDigit}]))?(\x{2c}(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}])))+))|(((([0-9\p{javaDigit}]))++)|(\p{javaDigit}&&[^0]?(([0-9\p{javaDigit}]))?(\x{2c}(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}])))+))\x{2e}(([0-9\p{javaDigit}]))+|\x{2e}(([0-9\p{javaDigit}]))++)([eE][+-]?(([0-9\p{javaDigit}]))+)?)|(\Q-\E((((([0-9\p{javaDigit}]))++)|(\p{javaDigit}&&[^0]?(([0-9\p{javaDigit}]))?(\x{2c}(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}])))+))|(((([0-9\p{javaDigit}]))++)|(\p{javaDigit}&&[^0]?(([0-9\p{javaDigit}]))?(\x{2c}(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}]))(([0-9\p{javaDigit}])))+))\x{2e}(([0-9\p{javaDigit}]))+|\x{2e}(([0-9\p{javaDigit}]))++)([eE][+-]?(([0-9\p{javaDigit}]))+)?))|[-+]?0[xX][0-9a-fA-F].[0-9a-fA-F]+([pP][-+]?[0-9]+)?|(([-+]?(NaN|\QNaN\E|Infinity|\Q∞\E))|((NaN|\QNaN\E|Infinity|\Q∞\E))|(\Q-\E(NaN|\QNaN\E|Infinity|\Q∞\E)))