views:

392

answers:

7

Consider the need for a function in C# that will test whether a string is a numeric value.

The requirements:

  • must return a boolean.
  • function should be able to allow for whole numbers, decimals, and negatives.
  • assume no using Microsoft.VisualBasic to call into IsNumeric(). Here's a case of reinventing the wheel, but the exercise is good.

Current implementation:

 //determine whether the input value is a number
    public static bool IsNumeric(string someValue)
    {
        Regex isNumber = new Regex(@"^\d+$");
        try
        {
            Match m = isNumber.Match(someValue);
            return m.Success;                           
        }
        catch (FormatException)
        {return false;}
    }

Question: how can this be improved so that the regex would match negatives and decimals? Any radical improvements that you'd make?

+5  A: 
Regex isNumber = new Regex(@"^[-+]?(\d*\.)?\d+$");

Updated to allow either + or - in front of the number.

Edit: Your try block isn't doing anything as none of the methods within it actually throw a FormatException. The entire method could be written:

// Determine whether the input value is a number
public static bool IsNumeric(string someValue)
{
  return new Regex(@"^[-+]?(\d*\.)?\d+$").IsMatch(someValue);
}
Sean Bright
Be advised this has Turkey Test issues (see #4 on http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html )
Jeff Moser
Will not work if the number is using ',' as a decimal separator (as we do in Sweden for instance).
Fredrik Mörk
I hadn't noticed that the solution needed to be locale aware. Must have been in a previous edit.
Sean Bright
@Sean: should not all software be, by default?
Fredrik Mörk
@Fredrik - Nope.
Sean Bright
@Sean, @Fredrik, software developers, by default, shouldn't try to invent the wheel
azheglov
+18  A: 

Just off of the top of my head - why not just use double.TryParse ? I mean, unless you really want a regexp solution - which I'm not sure you really need in this case :)

Saulius
+1, right tool for the job, etc.
James M.
@LFSR The example for the user, they are looking for just a number, IMHO Double.TryParse would be much more appropriate.
Mitchel Sellers
@Mitchel - Yes, I noticed that, I changed the wording of the question.
Gavin Miller
This should also handle any internationalization issues (comma vs. period)
Jesse
+1  A: 

Well, for negatives you'd need to include an optional minus sign at the start:

^-?\d+$

For decimals you'd need to account for a decimal point:

^-?\d*\.?\d*$

And possible exponential notation:

^-?\d*\.?\d*(e\d+)?$
Joey
Will not work for locales using ',' as a decimal separator.
Fredrik Mörk
locales are an entirely different problem and once you start worrying about *that* you also have a problem with the assumption that U+002D HYPHEN-MINUS is your minus sign (it's not for my settings here, as I prefer using U+2212 MINUS SIGN, for example). It's not a task you can do reliably with regex, I think.
Joey
+9  A: 

Can you just use .TryParse?

int x;
double y;
string spork = "-3.14";

if (int.TryParse(spork, out x))
    Console.WriteLine("Yay it's an int (boy)!");
if (double.TryParse(spork, out y))
    Console.WriteLine("Yay it's an double (girl)!");
yodaj007
A: 

Unless you really want to use regex, Noldorin posted a nice extension method in another Q&A.

Update

As Patrick rightly pointed out, the link points to an extension method that check whether the object is a numeric type or not, not whether it represents a numeric value. Then using double.TryParse as suggested by Saulius and yodaj007 is probably the best choice, handling all sorts of quirks with different decimal separators, thousand separators and so on. Just wrap it up in a nice extension method:

public static bool IsNumeric(this string value)
{
    double temp;
    return double.TryParse(value.ToString(), out temp);
}

...and fire away:

string someValue = "89.9";
if (someValue.IsNumeric()) // will be true in the US, but not in Sweden
{
    // wow, it's a number!
]
Fredrik Mörk
excellent suggestion!
p.campbell
given that the input is a string, this method won't work
Patrick McDonald
@Patrick: true! Didn't think about that; his method tested if the type was numeric, not string content.
Fredrik Mörk
A: 

I can't say that I would use regular expressions to check if a string is a numeric value. Slow and heavy for such a simple process. I would simply run over the string one character at a time until I enter an invalid state:

public static bool IsNumeric(string value)
{
    bool isNumber = true;

    bool afterDecimal = false;
    for (int i=0; i<value.Length; i++)
    {
        char c = value[i];
        if (c == '-' && i == 0) continue;

        if (Char.IsDigit(c))
        {
            continue;
        }

        if (c == '.' && !afterDecimal)
        {
            afterDecimal = true;
            continue;
        }

        isNumber = false;
        break;
    }

    return isNumber;
}

The above example is simple, and should get the job done for most numbers. It is not culturally sensitive, however, but it should be strait-forward enough to make it culturally sensitive.

jrista
The problem with this approach is the handling of globalized numbers (which is *not* straightforward), exponential notations, and values like this: +3.14.14.14-14The regex might be slower, but it's also a lot simpler and much more easily understood.
yodaj007
@jrista: hand-coding an IsNumeric function is not a simple process. The only time it may be simple is if you know for sure that the code will be run in one specific locale. But the it may break as soon as some foreign user changes the regional settings in Windows.
Fredrik Mörk
@Fredrik: Sure, I understand that my example is not culturally sensitive (and I noted such). However, the question called for a new implementation (reinvention of the wheel), and I was simply demonstrating that it could be done by processing the string once character at a time. If culture-sensitivity is required (it was not asked for in the question posed), .NET is riddled with culture-related functionality that should make it fairly strait forward to incorporate.
jrista
A: 

Also, make sure the resulting code passes the Turkey Test:
http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html

Tormod Fjeldskår