[c#] Regular expression for decimal number

I need to validate a textbox input and can only allow decimal inputs like: X,XXX (only one digit before decimal sign and a precision of 3).

I'm using C# and try this ^[0-9]+(\.[0-9]{1,2})?$?

This question is related to c# regex decimal

The answer is


^[0-9]([.,][0-9]{1,3})?$

It allows:

0
1
1.2
1.02
1.003
1.030
1,2
1,23
1,234

BUT NOT:

.1
,1
12.1
12,1
1.
1,
1.2345
1,2345

In general, i.e. unlimited decimal places:

^-?(([1-9]\d*)|0)(.0*[1-9](0*[1-9])*)?$


\d{1}(\.\d{1,3})?

Match a single digit 0..9 «\d{1}»
   Exactly 1 times «{1}»
Match the regular expression below and capture its match into backreference number 1 «(\.\d{1,3})?»
   Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
   Match the character “.” literally «\.»
   Match a single digit 0..9 «\d{1,3}»
      Between one and 3 times, as many times as possible, giving back as needed (greedy) «{1,3}»


Created with RegexBuddy

Matches:
1
1.2
1.23
1.234


I just found TryParse() has an issue that it accounts for thousands seperator. Example in En-US, 10,36.00 is ok. I had a specific scenario where the thousands seperator should not be considered and hence regex \d(\.\d) turned out to be the best bet. Of course had to keep the decimal char variable for different locales.


There is an alternative approach, which does not have I18n problems (allowing ',' or '.' but not both): Decimal.TryParse.

Just try converting, ignoring the value.

bool IsDecimalFormat(string input) {
  Decimal dummy;
  return Decimal.TryParse(input, out dummy);
}

This is significantly faster than using a regular expression, see below.

(The overload of Decimal.TryParse can be used for finer control.)


Performance test results: Decimal.TryParse: 0.10277ms, Regex: 0.49143ms

Code (PerformanceHelper.Run is a helper than runs the delegate for passed iteration count and returns the average TimeSpan.):

using System;
using System.Text.RegularExpressions;
using DotNetUtils.Diagnostics;

class Program {
    static private readonly string[] TestData = new string[] {
        "10.0",
        "10,0",
        "0.1",
        ".1",
        "Snafu",
        new string('x', 10000),
        new string('2', 10000),
        new string('0', 10000)
    };

    static void Main(string[] args) {
        Action parser = () => {
            int n = TestData.Length;
            int count = 0;
            for (int i = 0; i < n; ++i) {
                decimal dummy;
                count += Decimal.TryParse(TestData[i], out dummy) ? 1 : 0;
            }
        };
        Regex decimalRegex = new Regex(@"^[0-9]([\.\,][0-9]{1,3})?$");
        Action regex = () => {
            int n = TestData.Length;
            int count = 0;
            for (int i = 0; i < n; ++i) {
                count += decimalRegex.IsMatch(TestData[i]) ? 1 : 0;
            }
        };

        var paserTotal = 0.0;
        var regexTotal = 0.0;
        var runCount = 10;
        for (int run = 1; run <= runCount; ++run) {
            var parserTime = PerformanceHelper.Run(10000, parser);
            var regexTime = PerformanceHelper.Run(10000, regex);

            Console.WriteLine("Run #{2}: Decimal.TryParse: {0}ms, Regex: {1}ms",
                              parserTime.TotalMilliseconds, 
                              regexTime.TotalMilliseconds,
                              run);
            paserTotal += parserTime.TotalMilliseconds;
            regexTotal += regexTime.TotalMilliseconds;
        }

        Console.WriteLine("Overall averages: Decimal.TryParse: {0}ms, Regex: {1}ms",
                          paserTotal/runCount,
                          regexTotal/runCount);
    }
}

As I tussled with this, TryParse in 3.5 does have NumberStyles: The following code should also do the trick without Regex to ignore thousands seperator.

double.TryParse(length, NumberStyles.AllowDecimalPoint,CultureInfo.CurrentUICulture, out lengthD))

Not relevant to the original question asked but confirming that TryParse() indeed is a good option.


In .NET, I recommend to dynamically build the regular expression with the decimal separator of the current cultural context:

using System.Globalization;

...

NumberFormatInfo nfi = NumberFormatInfo.CurrentInfo;
Regex re = new Regex("^(?\\d+(" 
                   + Regex.Escape(nfi.CurrencyDecimalSeparator) 
                   + "\\d{1,2}))$");

You might want to pimp the regexp by allowing 1000er separators the same way as the decimal separator.