-4

I have coded a piece of code that takes the expression from the user:

(- ( / 3 4) 0.5)

removes all the spaces like this:

(-(/34)0.5)

and saves it to a string array, after which they are pushed to stack

The code is as follows:

Scanner sc = new Scanner(System.in);
s = sc.nextLine();
sc.close();

s = s.replaceAll("\\s+",""); 
String [] x = s.trim().split("");

exprStack = new Stack<String>();

for (int i=0;i<x.length;i++){
         exprStack.push(x[i]);//prints what goes to stack
     System.out.println(x[i]);
}

However, when I print the elements of array that are pushed to stack the double digit ones split, like 0.5 The output is:

(
-
(
/
3
4
)
0
.
5
)
compcrk
  • 411
  • 4
  • 9
  • 18
  • 5
    *when I print the elements of array that are pushed to stack the '0.5' is split to '0' and '.5'* No. It's split to `0`, `.` and `5`. Try `(+ 42 100)` with your current algorithm too. – Elliott Frisch Apr 14 '16 at 17:14
  • 2
    The `.split("")` expression will return a list of EVERY individual character. It will divide your `0.5` into three separate Strings (`[ "0", ".", "5" ]`). – Mr. Polywhirl Apr 14 '16 at 17:18
  • 1
    Of course `0.5` splits, you're splitting each character of `s` and `0`, `.`, and `5` are all valid characters. – GrizzlyManBear Apr 14 '16 at 17:18
  • Possible duplicate of [How to Split a mathematical expression on operators as delimiters, while keeping them in the result?](http://stackoverflow.com/questions/13525024/how-to-split-a-mathematical-expression-on-operators-as-delimiters-while-keeping) – Mr. Polywhirl Apr 14 '16 at 17:54

2 Answers2

1

I think your intention is to read elements and add to stack. If you are using string it will cause the problem currently you are facing. Better approach is to read each element one by one and add to stack. Then it will not split the decimal numbers. Another approach is to specify every elements space seperated and read them by Scanner next() method.

Tapan Prakash
  • 773
  • 8
  • 7
1

The easiest way to achieve this is to use a positive lookaheads and lookbehinds to find valid operators and spaces between numbers.

After you have those tokens, just filter out any left-over spaces.

/[ ]/               // 
/(?<=[-+*/\(\) ])/  // Positive Lookbehind
/(?=[-+*/\(\) ])/   // Positive Lookahead
import java.util.Arrays;

public class Tokenizer {
    public static String[] tokenize(String expression) {
        String[] tokens = expression.split("(?<=[-+*/\\(\\) ])|(?=[-+*/\\(\\) ])");
        String[] result = new String[tokens.length];
        int i = 0;

        for (String token : tokens) {
            if (!token.equals(" ")) {
                result[i++] = token;
            }
        }

        return Arrays.copyOf(result, i);
    }

    public static void main(String[] args) {
        String[] tokens = tokenize("(- ( / 8 14) 0.5)");

        for (int i = 0; i < tokens.length; i++) {
            System.out.printf("%2d.  %s%n", i, tokens[i]);
        }
    }
}

Result

 0.  (
 1.  -
 2.  (
 3.  /
 4.  8
 5.  14
 6.  )
 7.  0.5
 8.  )

The best approach is to use a StringTokenizer to parse through your string. You can find an example of this here:

Stack Overflow: How to Split a mathematical expression on operators as delimiters, while keeping them in the result?


Update

I wrote a simple parser to intelligently figure out what is white-space, a digit, or an operand. If you want to go the extra step, take a look at ANTLR or another grammar/language parser.

Using Scanner

This method is pretty much the same as the code above, except there is no post-processing with eliminating spaces. They are just gobbled up by the scanner.

import java.util.*;

public class MathExpressionParser {
    public static void main(String[] args) {
        String[] tokens = parseExpression("(- ( / 8 14) 0.5)");

        for (int i = 0; i < tokens.length; i++) {
            System.out.printf("%2d.  %s%n", i, tokens[i]);
        }
    }

    public static String[] parseExpression(String expression) {
        List<String> result = new ArrayList<String>();
        Scanner scanner = new Scanner(expression);

        scanner.useDelimiter("[ ]+(?<=[-+*/\\(\\) ])|(?=[-+*/\\(\\) ])");

        while (scanner.hasNext()) {
            result.add(scanner.next());
        }
        scanner.close();

        return result.toArray(new String[result.size()]);
    }
}

Using Custom Parser

This is the best way to go. Regular expressions can get pretty big and hard to manage, also they kill performance. If you exactly what your criteria is, go ahead and parse it yourself. It will speed up performance and you can easily add new conditions when you see fit.

import java.util.*;

public class MathExpressionParser {
    public static void main(String[] args) {
        String[] tokens = parseExpression("(- ( / 8 14) 0.5)");

        for (int i = 0; i < tokens.length; i++) {
            System.out.printf("%2d.  %s%n", i, tokens[i]);
        }
    }
    public static String[] parseExpression(String expression) {
        List<String> result = new ArrayList<String>();
        StringBuffer buffer = new StringBuffer();

        for (char ch : expression.toCharArray()) {
            if (Character.isWhitespace(ch)) {
                bufferToItem(buffer, result);
            } else {
                if (Character.isDigit(ch) || ch == '.') {
                    buffer.append(ch);
                } else {
                    bufferToItem(buffer, result);
                    result.add(Character.toString(ch));
                }
            }
        }

        return result.toArray(new String[result.size()]);
    }

    public static void bufferToItem(StringBuffer buffer, List<String> list) {
        addItemIf(list, buffer);
        clearBuffer(buffer);
    }

    public static void addItemIf(List<String> list, StringBuffer buffer) {
        if (buffer.length() > 0) list.add(buffer.toString());
    }

    public static void clearBuffer(StringBuffer buffer) {
        buffer.delete(0, buffer.length());
    }
}
Community
  • 1
  • 1
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132