tags:

views:

4667

answers:

5

Hi all,

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#

Should it be something like ^[0-9]+(.[0-9]{1,2})?$

Thanks!!!

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

It allow:

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
J-16 SDiZ
Replacing [0-9] with \d cuts out 3 characters (6 total) and does the same thing.
Unkwntech
+1 but wouldn't it be good to accept .1 as this is a valid representation of 0.1 (tested with Decimal.Parse)?
Stevo3000
The simplest version of this would be "\d(\.\d{1,3})?" but using "\d(?:\.\d{1,3})?" will mean that a group is not stored.
Stevo3000
This will work only if the locale settings of the user uses . as a decimal separator. It will break if it's , (in fr-FR, for instance...)
Yann Schwartz
I've updated it to allow the , delimited decimal format
Unkwntech
+1  A: 
\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

Unkwntech
you can even leave out the {1}
tanascius
I realized that, and edited to reflect the change.
Unkwntech
You still have \d{1} instead of \d
Stevo3000
+5  A: 

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);
    }
}
Richard
That’s not only faster, it’s much cleaner.
Konrad Rudolph
A: 

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. Ofcourse had to keep the decimal char variable for different locales.

Rajesh
Use the overload of `Decimal.TryParse` that takes a `NumerStyles` parameter, and *don't* include `NumerStyles.AllowThousands`.
Richard
A: 

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.

rvanchis