解析字符串以忽略空格,Calculator类时出错

时间:2014-07-31 22:35:53

标签: java regex parsing

我正在学习Java,而且我一直在编写计算器类。目前,它仅用于使用两个数字进行简单操作,例如2 + 2.以下是我的代码的一小部分:

public static void start()
{
    System.out.println("Calculator. Type \"exit\" to quit.");
    System.out.print("> ");

    Scanner scan = new Scanner(System.in);       
    String entry = scan.nextLine();

    while (! (entry.equals("exit")))
    {           
        String [] values = entry.replaceAll("(^\\s+|\\s+$)", "").split("\\s+");

        num1 = new Double(values[0]);
        operand = values[1];
        num2 = new Double(values[2]);
        ...
    }
    ...
}
...

之后出现一个switch语句,它是while循环的一部分。 switch语句测试operand的值,调用适当的方法,打印结果,然后重新启动while循环。

我自己并没有对正则表达式部分进行编码,但是通过阅读之前的StackOverlfow帖子,我了解到它会忽略任何空白字符之前,中间和之后我想要的值。

代码正常,但空格字符不分隔num1operandnum2的情况除外。

例如:

> 2 + 2

4.0

>2 + 2

4.0

但是:

>2+2

Exception in thread "main" java.lang.NumberFormatException: For input string: "2+2"
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1250)
    at java.lang.Double.valueOf(Double.java:504)
    at java.lang.Double.<init>(Double.java:597)
    at calc.Calc.start(Calc.java:24)
    at calc.CalcClient.main(CalcClient.java:8)
Java Result: 1

并且:

>2+ 2

Exception in thread "main" java.lang.NumberFormatException: For input string: "2+"
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1250)
    at java.lang.Double.valueOf(Double.java:504)
    at java.lang.Double.<init>(Double.java:597)
    at calc.Calc.start(Calc.java:24)
    at calc.CalcClient.main(CalcClient.java:8)
Java Result: 1

并且:

>2 +2

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
    at calc.Calc.start(Calc.java:26)
    at calc.CalcClient.main(CalcClient.java:8)
Java Result: 1

我的错误在哪里?它是数组的问题,我解析字符串的方式,或者其他可能的东西?我想对它进行编码,以便用户可以根据需要输入具有尽可能多的空白的内容,或者根本不输入内容。

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

答案仅作为示例提供:

    // extract the operands and operator
    String[] operandsArrStr = "22+ 2".replaceAll("\\s+", "").split("[\\+\\-\\/\\*]"); // {"22", "2"}
    String operator = "22+ 2".replaceAll("(\\s+|\\d+|\\.)", ""); // "+"

    // convert the values to doubles
    Double[] operands = new Double[operandsArrStr.length];
    for (int i=0; i < operandsArrStr.length; i++) {
        operands[i] = new Double(operandsArrStr[i]);
    }

    Double res = null;
    // calc
    if ("+".equals(operator)) {
        res = operands[0] + operands[1]; // assuming only two operands and one operator!
    }
    else if ("-".equals(operator)) {
        res = operands[0] - operands[1];
    }
    else if ("/".equals(operator)) {
        res = operands[0] / operands[1];
    }
    else if ("*".equals(operator)) {
        res = operands[0] * operands[1];
    }
    System.out.println("res = " + res); // prints 24.0

答案 1 :(得分:0)

这是一个正则表达式问题,实际上是:)

您应该替换该行:

String [] values = entry.replaceAll("(^\\s+|\\s+$)", "").split("\\s+");

... by:

String [] values = entry.trim().split("\\s*");

<强> 说明:

  • 实际上,String#trim会删除String开头和结尾处的所有最终空格。我相信你正在尝试使用.replaceAll("(^\\s+|\\s+$)", "")
  • 其次,正则表达式中的\s+表示“至少有一个空白字符”,而\s*表示“任何序列白色空格字符“(包括没有空白区域,这对您来说是有效的案例;)

但是,我实际上建议你尝试不同的方法。请考虑下面的工作代码示例:

private static final Pattern SIMPLE_OPERATION = Pattern.compile("(?<num1>\\d*\\.?\\d+)(?<operand>[+-/*])(?<num2>\\d*\\.?\\d+)");

public static void start() {
    System.out.println("Calculator. Type \"exit\" to quit.");

    try (final Scanner scan = new Scanner(System.in)) {
        Double num1, num2;
        String operand;
        String entry;

        do {
            System.out.print("> ");
            entry = scan.nextLine();

            final Matcher m = SIMPLE_OPERATION.matcher(entry.replaceAll("\\s+", ""));
            if (m.matches()) {
                num1 = Double.valueOf(m.group("num1"));
                num2 = Double.valueOf(m.group("num2"));
                operand = m.group("operand");

                System.out.println(String.format("'%s', '%s', '%s'", num1, operand, num2));
            }
        } while (!(entry.equalsIgnoreCase("exit")));
    }
}

这可以在将来轻松增强,并且可以处理以下任何事情:

2+2
  2  * 2 
2 -  2
2 / 2

...还有十进制数字:

88     +   65.005555
.5*2

我希望这会帮助你开始;)

答案 2 :(得分:0)

以下代码可以在独立的可执行文件中使用并附带测试用例:

import java.util.*;
import java.util.regex.*;

class Foo {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("\\s*(\\d+)\\s*(\\D)\\s*(\\d+)");
        String[] tests = {"2+2", "2 + 2", "   2 +     2", "2    +2", "2 +2", "2+ 2"};
        for (String entry : tests) {
            System.out.println("testing entry: " + entry);
            Matcher matcher = pattern.matcher(entry);
            if (matcher.matches()) {
                Double num1 = new Double(matcher.group(1));
                String operand = matcher.group(2);
                Double num2 = new Double(matcher.group(3));
                System.out.printf("num1: %s, operand: %s, num2: %s\n", num1, operand, num2);
            }

        }
    }
}

然而,这段代码相当脆弱,因为它仅适用于您指定的确切格式,即带整数的二进制运算。如果您想要获得更高级的功能,您可能要么使用扫描仪,要么考虑使用像ANTLR这样的完整解析器。 ANTLR在这种情况下非常容易使用,事实上我认为ANTLR教程涵盖了创建计算器。

编辑:此代码无法处理浮点数,即使它将它们存储在双打中。