Firefox isn't the problem. The pattern is incomplete.
Try this pattern instead:
@"^\s?((?<qty>\d+)\s?/)?\s?[$]?\s?(?<price>[0-9]{1,2}\.?[0-9]?[0-9]?)"
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 was 4/$3.99
but 4/$33.99
would 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. For 77.77
it 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 only 77
. 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 price
group 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 .xy
in the number? My proposed pattern at the top used [0-9]{1,2}
to force 1-2 numbers to exist, while leaving the .xy
portion optional.
If the .xy
portion is truly optional you could update your price
group 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:
@"^\s?((?<qty>\d+)\s?/)?\s?[$]?\s?(?<price>\d{1,2}(?:\.\d{1,2})?)"
Notice that the pattern still has issues, depending on what your requirements are. The entire qty
group is optional, meaning the 4/
part could be omitted from the input and an input of $3.99
would be valid. If this is required then don't make it optional:
@"^\s?((?<qty>\d+)\s?/)\s?[$]?\s?(?<price>\d{1,2}(?:\.\d{1,2})?)"