473,386 Members | 1,827 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

String Parsing and Brackets

balabaster
797 Expert 512MB
I'm looking for some ideas regarding string parsing and brackets.

Say I have the following string:

56*(73+23/(28+(7/14)-(3/2))

What would be the best way to parse the string for each opening bracket's closing bracket? Is there a standard way of doing this out there somewhere that I've failed to find?

As you can see by parsing the string visually, we're missing a closing bracket, and a simple count of opening braces and closing braces to check for a match will tell us one is missing

However, I'd like to highlight each set of braces when the cursor is inside a pair of brackets, or if I type a bracket, it automatically bolds it's corresponding pair. For instance, if I type a closing bracket, it's corresponding opening bracket is bolded, or if I change the position of the cursor, any brackets bounding the current cursor position is bolded.

Is this something that can easily be done using some application of regular expressions for instance that I have missed?

If anyone has a simple method of doing this, I would appreciate some insight into the method they used.
Aug 27 '08 #1
9 4535
cloud255
427 Expert 256MB
Well counting braces i guess is the way to go, i would look for the last "(" brace first with the LastIndexOf() method and match that with the first ")" brace.
Then make a substring which excludes everything between that pair of braces. and repeat the process. Look into recursion for this.

This actaully looks like a really good exercise. if you get stuck/find a solution would you mind posting the code, i would love to have a look at it.
Aug 27 '08 #2
balabaster
797 Expert 512MB
Well counting braces i guess is the way to go, i would look for the last "(" brace first with the LastIndexOf() method and match that with the first ")" brace.
Then make a substring which excludes everything between that pair of braces. and repeat the process. Look into recursion for this.

This actaully looks like a really good exercise. if you get stuck/find a solution would you mind posting the code, i would love to have a look at it.
If I'm going to be parsing the string, then for sure recursion looks like the ideal solution. The LastIndexOf() method won't work as the last bracket doesn't always pair with the first...

Consider the following example:

(2+3)*(3+(27/(3*26)))

The final bracket actually matches with the second opening bracket in this case. I think the trick is to recurse until there are no more brackets inside any given pair and then work your way outwards, much like recursion through the file system using a tree or parsing XML or the DOM.

I'm still doing some research, but I will post information as I find that it is useful for this exercise.
Aug 27 '08 #3
cloud255
427 Expert 256MB
If I'm going to be parsing the string, then for sure recursion looks like the ideal solution. The LastIndexOf() method won't work as the last bracket doesn't always pair with the first...

Consider the following example:

(2+3)*(3+(27/(3*26)))
I see what you mean, a slight modification, still using the lastIndexOf() and then going the first ")" after that index would give you everything between those two brackets and then highlighting that would make any mistakes clearly visible and also conform to the mathematical order of operations (i think)...

But i am pretty confident that a better solution is possible, i'll do some thinking about analyzing the string as a char array and looking at individual elements between 'brace indicies'.
Aug 27 '08 #4
Plater
7,872 Expert 4TB
You might be able to look up like "mathmatical regex" and find something that would chunk your string into sections?


(2+3)*(3+(27/(3*26)))

a=3*6
b=27/a
c=3+b
d=2+3
final=d*c
Aug 27 '08 #5
balabaster
797 Expert 512MB
You might be able to look up like "mathmatical regex" and find something that would chunk your string into sections?


(2+3)*(3+(27/(3*26)))

a=3*6
b=27/a
c=3+b
d=2+3
final=d*c
I'm just investigating that route as we speak... however, the mathematical example was merely an example to demonstrate the need... it's final application won't be strictly mathematical, but will also hold some logical functions, for instance: Min, Max, Avg, Median, In, And, Or, Not

The syntax is not dissimilar to Excel's spreadsheet functions to provide an expression builder for a piece of software I'm writing for a client... This will allow them to code business rules right into their system without having to call me every 10 minutes to ask me to develop a rule and plug it in or change an existing rule. I'm basically trying to build a means of idiot proofing a concept that I haven't finished formulating yet.

A basic example of the expression that could be parsed:
Max(10, 0.1 * (@Value - (@Weight * 2.75)))

Where @Value and @Weight are variables that the user will be prompted for information.

The result of the final calculation would be the highest of $10 or 10% of any value exceeding $2.75 per lb. Essentially providing a standard formula for calculating a value above a minimum charge...

Or another example:

IF(@Value < 5, 0.25, 0)

This could calculate a surcharge of 25c if the amount entered by the user is less than $5.

Or yet another example:

IF(@Value IN (10, 4, 2, 27, 3), 4.5, 2.75)

If the value entered falls within items in the in list, then add a charge of $4.50, otherwise add a charge of $2.75. The IN operator checks a list that may also contain expressions, so expressions could get quite complex.

All that is irrelevant for the moment at this stage though, I'm not yet looking to build the calculation or obtain the result, but to provide some cosmetic solution to make it easier to read when it's being input into the input box (richtext). The method used however, will probably be the same
Aug 27 '08 #6
balabaster
797 Expert 512MB
Okay, for the mathematical portion of the subject I developed a bunch of regular expressions which can be plugged into a recursive function:

Using the mathematical principles of BoDMAS (Brackets, Division, Multiplication, Addition, Subtraction):

Brackets - Condense brackets out of the equation:
"\([^\(\)]*?\)" = Grab each phrase that doesn't contain any brackets...these can be evaluated as calculations, replace these phrases in the original string with the resulting calculation. Do this recursively until no brackets remain in your original phrase.

Evaluate a simple expression:
Now our phrase has been condensed down to a simple string containing just operands and operators (numbers and +-*\)
"(\d+(\.\d+)?)\s*[\\/\*+\-]\s*(\d+(\.\d+)?)"
Group 1 will contain the first operand
Group 2 will contain the fractional part of the first operand
Group 3 will contain the operator that may be / or \
Group 4 will contain the second operand
Group 5 will contain the fractional part of the second operand
When evaluating this phrase, you can eval the exact (or close to) output if the operator is / or just return the integer part of the result if the operator is \

Hey presto, you've got essentially two fairly small functions that can evaluate the whole phrase. Remember to prevent a user from entering more closing brackets than opening brackets or it could all fall to pieces.

Mental note: Don't forget which order to evaluate division, multiplication, addition and subtraction...

Now, on to including logic into the puzzle... I'll keep you posted.
Aug 28 '08 #7
balabaster
797 Expert 512MB
Okay, for those that care, I got this down to a single recursive function... and one helper extension for String.Replace to avoid having to build a whole stack and manage all that...

The code's a bit preliminary just now... I'm sure it can be condensed into something a little more elegant - each of the code blocks outside the condensing out of the brackets is so similar that I daresay I can create a separate function for them which is what I will work on next...

Expand|Select|Wrap|Line Numbers
  1.     Private Function Evaluate(ByVal Expression As String) As Double
  2.  
  3.         Dim oMatch As Match = Nothing
  4.  
  5.         'If we have any bracketed expressions, then condense them out...
  6.         Dim Pattern As String = "\(([^\(\)]*?\))"
  7.         While Regex.Matches(Expression, Pattern).Count > 0
  8.  
  9.             Dim oRegex As New Regex("\(([^\(\)]*)?\)")
  10.             oMatch = oRegex.Match(Expression)
  11.             Expression = Expression.Replace(oMatch.Index, oMatch.Length, Evaluate(oMatch.Groups(1).Value))
  12.  
  13.         End While
  14.  
  15.         'There are no brackets left, we're onto orders... i.e. powers and roots
  16.         Pattern = "(\d+(\.\d+)?)((\s*\^\s*)|(√\s*))(\d+(\.\d+)?)"
  17.         While Regex.Matches(Expression, Pattern).Count > 0
  18.  
  19.             Dim oMatches = Regex.Matches(Expression, Pattern)
  20.             For Each oMatch In oMatches
  21.  
  22.                 Dim Operand1 = oMatch.Groups(0).Value
  23.                 Dim [Operator] = oMatch.Groups(2).Value
  24.                 Dim Operand2 = oMatch.Groups(5).Value
  25.  
  26.                 Dim Result As Double = 0
  27.                 Select Case [Operator].ToLower
  28.                     Case "^" : Result = Math.Exp(Math.Log(Operand1) * Operand2)
  29.                     Case "√" : Result = Math.Exp(Math.Log(Operand2) - Math.Log(Operand1))
  30.                 End Select
  31.                 Expression = Expression.Replace(oMatch.Index, oMatch.Length, Result)
  32.             Next
  33.  
  34.         End While
  35.  
  36.         'All the brackets and orders have been processed, do the division and multiplication first
  37.         Pattern = "(\d+(\.\d+)?)\s*([\\/*×])\s*(\d+(\.\d+)?)" 'All expressions containing / \ or * or ×
  38.         While Regex.Matches(Expression, Pattern).Count > 0
  39.  
  40.             Dim oMatches = Regex.Matches(Expression, Pattern)
  41.             For Each oMatch In oMatches
  42.  
  43.                 Dim Operand1 = oMatch.Groups(1).Value
  44.                 Dim [Operator] = oMatch.Groups(3).Value
  45.                 Dim Operand2 = oMatch.Groups(4).Value
  46.  
  47.                 Dim Result As Double = 0
  48.                 If IsNumeric(Operand1) And IsNumeric(Operand2) Then
  49.                     Select Case [Operator].ToLower
  50.                         Case "/", "÷" : Result = CDbl(Operand1) / CDbl(Operand2)
  51.                         Case "\" : Result = CDbl(Operand1) \ CDbl(Operand2)
  52.                         Case "*", "×" : Result = CDbl(Operand1) * CDbl(Operand2)
  53.                     End Select
  54.                     Expression = Expression.Replace(oMatch.Index, oMatch.Length, Result)
  55.                 End If
  56.  
  57.             Next
  58.  
  59.         End While
  60.  
  61.         'Now we're working left to right because we should only have + and - left
  62.         Pattern = "^(\d+(\.\d+)?)\s*([-+])\s*(\d+(\.\d+)?)" 'First expression in string...
  63.         Dim Total As Double = 0
  64.         While Regex.Matches(Expression, Pattern).Count > 0
  65.  
  66.             oMatch = Regex.Match(Expression, Pattern)
  67.             Dim Operand1 = oMatch.Groups(1).Value
  68.             Dim [Operator] = oMatch.Groups(3).Value
  69.             Dim Operand2 = oMatch.Groups(4).Value
  70.  
  71.             Dim Result As Double = 0
  72.             Select Case [Operator].ToLower
  73.                 Case "+" : Result = CDbl(Operand1) + CDbl(Operand2)
  74.                 Case "-" : Result = CDbl(Operand1) - CDbl(Operand2)
  75.             End Select
  76.  
  77.             Expression = Expression.Replace(oMatch.Index, oMatch.Length, Result)
  78.  
  79.         End While
  80.  
  81.         'Theoretically we should be down to just a solitary number now
  82.         If IsNumeric(Expression) Then
  83.             Return CDbl(Expression)
  84.         Else
  85.             Throw New Exception("Invalid expression: " & Expression)
  86.         End If
  87.  
  88.     End Function
Aug 28 '08 #8
balabaster
797 Expert 512MB
Okay, with a little bit of exception handling to make sure that the expression is of a valid form, the following code should reduce a calculation to it's result...
Expand|Select|Wrap|Line Numbers
  1. Private Function Evaluate(ByVal Expression As String) As Double
  2.  
  3.     Dim oMatch As Match = Nothing
  4.     Dim Pattern As String = "\(([^\(\)]*?\))"
  5.     While Regex.Matches(Expression, Pattern).Count > 0
  6.         oMatch = Regex.Match(Expression, Pattern)
  7.         Expression = Expression.Replace(oMatch.Index, _
  8.                                         oMatch.Length, _
  9.                                         Evaluate(oMatch.Groups(1).Value))
  10.     End While
  11.  
  12.     Dim Exprs() As String = New String() { _
  13.         "(\d+(\.\d+)?)\s*(\^)\s*(\d+(\.\d+)?)", _
  14.         "(\d+(\.\d+)?)?(√)\s*(\d+(\.\d+)?)", _
  15.         "(\d+(\.\d+)?)\s*([\\/*×])\s*(\d+(\.\d+)?)", _
  16.         "(\d+(\.\d+)?)\s*([-+])\s*(\d+(\.\d+)?)" _
  17.     }
  18.  
  19.     For Each Expr As String In Exprs
  20.         While Regex.Matches(Expression, Expr).Count > 0
  21.  
  22.             oMatch = Regex.Match(Expression, Expr)
  23.             Dim Operand1 = oMatch.Groups(1).Value
  24.             Dim [Operator] = oMatch.Groups(3).Value
  25.             Dim Operand2 = oMatch.Groups(4).Value
  26.  
  27.             Dim Result As Double = 0
  28.             Select Case [Operator].ToLower
  29.                 Case "^" : Result = Math.Pow(Operand1, Operand2)
  30.                 Case "√"
  31.                     If Operand1 = "" Then Operand1 = 2
  32.                     Result = Math.Pow(Operand2, (1 / CDbl(Operand1)))
  33.                 Case "/", "÷" : Result = CDbl(Operand1) / CDbl(Operand2)
  34.                 Case "\" : Result = CDbl(Operand1) \ CDbl(Operand2)
  35.                 Case "*", "×" : Result = CDbl(Operand1) * CDbl(Operand2)
  36.                 Case "+" : Result = CDbl(Operand1) + CDbl(Operand2)
  37.                 Case "-" : Result = CDbl(Operand1) - CDbl(Operand2)
  38.             End Select
  39.  
  40.             Expression = Expression.Replace(oMatch.Index, _
  41.                                             oMatch.Length, _
  42.                                             Result)
  43.  
  44.         End While
  45.     Next
  46.  
  47.     If IsNumeric(Expression) Then
  48.         Return CDbl(Expression)
  49.     Else
  50.         Throw New Exception("Invalid expression: " & Expression)
  51.     End If
  52.  
  53. End Function
You'll also need this module to extend the string datatype to replace by index and length:
Expand|Select|Wrap|Line Numbers
  1. Imports System.Runtime.CompilerServices
  2.  
  3. Module HelperExtensions
  4.  
  5.     <Extension()> _
  6.     Public Function Replace(ByVal SearchString As String, _
  7.                             ByVal StartIndex As Integer, _
  8.                             ByVal Length As Integer, _
  9.                             ByVal ReplaceString As String) As String
  10.  
  11.         SearchString = SearchString.Remove(StartIndex, Length)
  12.         SearchString = SearchString.Insert(StartIndex, ReplaceString)
  13.         Return SearchString
  14.  
  15.     End Function
  16.  
  17. End Module
Aug 28 '08 #9
balabaster
797 Expert 512MB
The regular expression pattern strings need to be modified to account for negative values...

(-?\d+(\.\d+)?)\s*[\\/*]\s*(-?\d+(\.\d+)?)

etc...

This will now handle negative numbers (well, if you're on a division or multiplication check)... all of patterns need to be changed to account for negatives. Percentage could be handled too, but then it starts getting messy because you have to account for percentage of what?

i.e.
4 + 3 * 12%...

is this interpreted as 4 plus 36% of 4 or 12% of 4 + 3?

What about 4 + 3 + 12% is this (4 + 3) + ((4 + 3) x 0.12) i.e. (4 + 3) * 1.12 or is this 4 + 3 + 0.12. What if the equation is more complex - what does the % really apply to.

What if you do 12 % 4 - is this now a modulator - i.e. 12 Mod 4 = 0?

Hence, I'm not sure exactly how I want to handle percentage yet...
Sep 3 '08 #10

Sign in to post your reply or Sign up for a free account.

Similar topics

2
by: Adrian Cornish | last post by:
Hi all, Is there a portable way of transforming a wchar_t to a char and/or wstring to a string. Are there any gurantees for the layout of a wchar_t, like every other byte is a char? I am...
11
by: Martin Robins | last post by:
I am trying to parse a string that is similar in form to an OLEDB connection string using regular expressions; in principle it is working, but certain character combinations in the string being...
5
by: Vjay77 | last post by:
Hi, there is one more problem I'd need your help with. I have log file with line like this one: 3/2/2004 12:09:31 AM - ( 52400) +OK Mailbox locked and ready I need to extract the number...
4
by: Emilio | last post by:
Question about Shared Sub Connect(server As , message As ) Why is in square brackets? Is it like Shared Sub Connect(server() As String, message() As String)
5
by: gamehack | last post by:
Hi all, I was thinking about parsing equations but I can't think of any generic approach. Basically I have a struct called math_term which is something like: struct math_term { char sign; int...
12
by: karoly.kiripolszky | last post by:
Helo ppl! At the job I was given the task to make a script to analyze C++ code based on concepts my boss had. To do this I needed to represent C++ code structure in Python somehow. I read the...
7
by: John Nagle | last post by:
Is there something available that will parse the "netloc" field as returned by URLparse, including all the hard cases? The "netloc" field can potentially contain a port number and a numeric IP...
6
by: Jasper | last post by:
Hi, Maybe this is off-topic, but perhaps you can help. I'm looking for ideas on how to parse a data file. I dont know XML but I know it parses data in text format. I have a structured data...
6
by: James Arnold | last post by:
Hello, I am new to C and I am trying to write a few small applications to get some hands-on practise! I am trying to write a random string generator, based on a masked input. For example, given...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.