I have the following C# code which should match a quantity / $ price string like “4/$3.99”. It works all day long until we use it against a string returned from Firefox Browser. 77.77 becomes 77 (dropping the .77 cents).
var matches = Regex.Match(_priceText,
@"^\s?((?<qty>\d+)\s?/)?\s?[$]?\s?(?<price>[0-9]?\.?[0-9]?[0-9]?)");
if( matches.Success)
{
if (!Decimal.TryParse(matches.Groups["price"].Value, out this._price))
this._price = 0.0m;
if (!Int32.TryParse(matches.Groups["qty"].Value, out this._qty))
this._qty = (this._price > 0 ? 1 : 0);
else
if (this._price > 0 && this._qty == 0)
this._qty = 1;
}
Any idea why the period wouldn’t match coming from a Firefox string, but the C# string matches? There isn’t any special about the Firefox we used. It’s a plain Jane 1252 code page download right off the Firefox site. The computer’s local settings are unaltered North American, etc. We have two different computers showing the same effects. It’s Firefox 3.6.4, nothing fancy or beta.
Firefox isn’t the problem. The pattern is incomplete.
Try this pattern instead:
The problem in the original pattern is the
(?<price>[0-9]?\.?[0-9]?[0-9]?)portion. The problem you described occurs with any number that starts with 2 digits, not just Firefox values. Your sample was4/$3.99but4/$33.99would cause the same issue. The[0-9]?\.?[0-9]?[0-9]?part matches a digit followed by a period. Unfortunately the pattern is littered with optional?metacharacters after almost everything and that is why this bug has popped up. For77.77it matches the first 7 then it should match a dot but wait, there’s a second 7 and no dot (which is optional\.?) so it happily skips it. Next the pattern expects 2 optional digits, but it sees a dot and stops, thus returning only77. That’s the general idea.Having said that, you should lay out precisely what inputs are valid when constructing the pattern. Your original pattern indicates that the
pricegroup is entirely optional. Look at it closely; everything has a?appended to it. So what are your goals? Is it optional? Are whole numbers allowed? Must it be a decimal with a.xyin the number? My proposed pattern at the top used[0-9]{1,2}to force 1-2 numbers to exist, while leaving the.xyportion optional.If the
.xyportion is truly optional you could update yourpricegroup to this:(?<price>\d{1,2}(?:\.\d{1,2})?)– that way the optional?metacharacter applies to everything that is optional and is only specified once. This makes the pattern more readable IMO. The(?:...)portion is optional (specifically usage of?:not the actual grouping) but it’s good practice to avoid capturing the group unnecessarily. With these changes in place the new pattern would be:Notice that the pattern still has issues, depending on what your requirements are. The entire
qtygroup is optional, meaning the4/part could be omitted from the input and an input of$3.99would be valid. If this is required then don’t make it optional: