Carol.Kar
Carol.Kar

Reputation: 5355

Tracking brackets in a string

I am having the following string and want to track the closing bracket of ROUND( ) in my string.

"=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18)))/$M$12;$M$11)";

public class RoundParser {

    public static String parseRound(String text) {

        text = text.toUpperCase();

        String result;

        char[] ch = text.toCharArray();
        int count = -1;
        String temp = "";
        for (int i = 0; i < ch.length; i++) {
            temp = temp + ch[i];

            System.out.println(count);

            if ("ROUND(".equals(temp)) {
                count++;
            }
            if ("(".equals(temp)) {
                count++;
            }
            if (")".equals(temp) && count > 0) {
                count--;
            }
            if (")".equals(temp) && count == 0) {
                ch[i] = '#';
            }
            if (!"ROUND(".startsWith(temp) || temp.length() > 5) {
                temp = "";
            }
        }

        text = String.valueOf(ch);

        result = text;
        return result;
    }

    public static void main(String[] args) {
        String text = "=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18)))/$M$12;$M$11)";

        System.out.println(parseRound(text));
    }

}

However, using my parser at the moment I am getting:

=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18))#/$M$12;$M$11#

The output I want to get is:

=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10#/$M$9;CA18*CB18)))/$M$12;$M$11#

As you can see the not the right ) are replaced, as ;$M$11)"; and ;$M$10). I really appreciate if you have any idea how to repalce these two cases.

Upvotes: 1

Views: 186

Answers (5)

Aradhna
Aradhna

Reputation: 983

there are 2 approaches to this

1) if the number of opening and closing brackets are always going to be equal, then you can just track the last closing bracket by using a for loop.

2) if you are not sure about opening and closing brackets to be equal, then you can so the following-->

public class RoundParser {

    public static String parseRound(String text) {

        text = text.toUpperCase();

        String result;

        char[] ch = text.toCharArray();

     int count=0,pos=0;
        int c[10];
        for(int i=0;i<ch.length;i++){

        if(ch[i].equals("(")){
        count++;  
         if(ch[i-1].equals("D")){
              c[pos]=count;   //will store the count value at every opening round
                  pos++;
              }
        }

        if(ch[i].equals(")")){
        count--;
         for(int j=0;j<10;j++){
             if(c[j]==count)   //if the closing of round had been encountered
                   ch[i]="#";   
         }
       }

        }


        text = String.valueOf(ch);

        result = text;
        return result;
    }

    public static void main(String[] args) {
        String text = "=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18)))/$M$12;$M$11)";

        System.out.println(parseRound(text));
    }

}

there you go.

i think this should work.

hope this helps.

Upvotes: 2

Khaled.K
Khaled.K

Reputation: 5950

For recognition of multiple ROUND(X), I suggest

TreeMap<Integer,Pair<Integer,Integer>> map = new TreeMap<>();

int count = 0;

Where we store <start_index, <init_count, end_index>>

if ("ROUND(".equals(temp))
{
    map.put(i, new Pair<Integer,Integer>(count, -1));
    count++;
}

if ("(".equals(temp)) count++;

if (")".equals(temp))
{
    if (count <= 0)
    {
        count = 0;
        // Error: extra closing bracket
    }
    else
    {
        count--;
    }

    int max_i = -1;

    for (Integer index : map.keySet())
    {
        if (index > max_i
          && map.get(index).second() == -1
          && map.get(index).first() == count)
        {
            max_i = index;
        }
    }

    if (max_i > -1) map.get(max_i).setSecond(i);
}

Upvotes: 1

Pham Trung
Pham Trung

Reputation: 11294

This problem can be done recursively.

First, you use method .indexOf("ROUND(") to detect the first occurrence of round().

Then, we need to determine which is the end ')' of this round(). A simple algo will be enough :

int start = text.indexOf("ROUND(") + "ROUND(".length();

int count = 1;
int end = -1;
for(int i = start; i < text.length; i++){ 
    if(text.charAt(i) == '('){
       count++;
    }else if(text.charAt(i) == ')'){
       count--;
    }
    if(count == 0){
       end = i;
       break;
    }
} 

After you detect the start and end of the outer round(), you can use text.substring(start, end) to remove the outer round(), and continue the above function recursively, until you find all round()

Upvotes: 2

TheLostMind
TheLostMind

Reputation: 36304

Here's an algorithm.. If you are not sure that the last ")" would be the one you are looking for. Start from index 0 of the String, for each "(" you encounter, increment the count, and for each ")" decrement the count, replace the ")" with "#".

Upvotes: 0

sina72
sina72

Reputation: 5101

You forgot an else:

else if (")".equals(temp) && count == 0) {

That will decrement count and if then count==0, it will decrement twice.

Upvotes: 2

Related Questions