views:

444

answers:

2

I'm using a math parser that uses % as the mod symbol. I'd like to use it for the percent symbol instead to allow users to type "25%*10" or "25% of 10" and receive the answer "2.5" (they could type anything). I'd then use the regex asked for to get the "25%" (could be in any part of the string) and do a simple calculation (25 / 100) and then replace the 25% in the string with the calculated value to pass to the math parser. Basically I'm doing some pre calculations on the number in front of the percent symbol before passing the full string to the math parser. I've always struggled with regex and was hoping someone could help me.

Thanks,

John

+5  A: 

Easiest way would be to let the math parser do the dividing:

s/([0-9.]+)%/($1\/100)/g

Which just replaces "25%" with "(25/100)" ... so if the user typed "25%*10" they'll now have "(25/100)*10" which when evaluated gives them the right answer.

Alternatively, in Perl, you could do:

s{([0-9.]+)%}{$1/100}eg

which would have Perl calculate the division by a hundred and pass that to the math parser

Sharkey
What about 12.5%?
Andy
Or (30+20)%? These get a bit harder. But that's not what the question is asking, I suppose.
Andy
@Andy - This is indeed a problem. Your examples take us from regex land into the parser land. Ideally, the parser should take a maximal mathematical expression in front of the % symbol, evaluate it and divide it by 100.0, to get a real fractional value. I believe this is impossible using a regular expression and requires something like a recursive descent parser.
Yuval F
Be careful with \d in Perl, as of 5.8 it means any UNICODE character that has the digit property. So unless you want to match U+1815 (MONGOLIAN DIGIT FIVE), you need to use [0-9].
Chas. Owens
@Chas: That's a really good point that I hadn't considered ...@Andy: Yeah, 12.5% would be pretty common, good point.I'll edit the post from (\d+) to ([0-9.]+)@Yuval: Yeah, if you try to go too far with these things you'd be better off modifying the parser.
Sharkey
@Iraimbilanja: Thanks for that, I hadn't noticed the runaway formatting.
Sharkey
@Sharkey: Can I get an example of using this to automatically do the replacement? I'm using C#. I've tried "Regex regex = new Regex(@"s/([0-9.]+)%/($1\/100)/g"); regex.IsMatch(equation);" and it returns false. I probably should've added the c# relation in the question.
John Rennemeyer
Using the following seems to work in simple testing: string finalExpression = Regex.Replace(expression, @"([0-9.]+)%", "($1/100)");If anyone sees anything wrong with this, please let me know.Thanks
John Rennemeyer
@John: That looks right, although I'm no C# expert. This http://www.devhood.com/tutorials/tutorial_details.aspx?tutorial_id=523 might be useful:
Sharkey
+1  A: 

The regular expression to find a number before a '%' sign is /(\d+)%/.

However, mathematical expressions are not a regular language, so a regular expression is likely not the right tool. You should instead tell your parser to interpret '%' as "take whatever comes before this and divide it by 100". "Whatever comes before this" can then be any mathematical expression, and you won't run into operator precedence problems, resp. you could sort these out in the syntax tree parser.

Mixing text substitution with real language features often violates the principle of least astonishment. When a user sees that '%' divides whatever comes before by 100, he might try to use expressions like (23+42)%, but that will just produce a syntax error. Also, you need a more elaborate regex if you have something like 1.34e-14%, but these things would just sort themselves out when you use the tree parser.

Svante
That's very true, and the syntax error, if you're passing it back from the parser to the user, is likely to be confusing ... with the regexp above, "(23+42)%" won't be substituted and "1.34e-14%" will be substituted to "1.34e-(14/100)" which is not terribly useful either.On the other hand, "23%+42%" would work fine.Yeah, if you need this level of complexity, you'd have the modify the parser.
Sharkey