I Need an IsNumeric Method

J

John Bowman

Hello,

Does anyone have a good/reliable approach to implementing an IsNumeric()
method that accepts a string that may represent a numerical value (eg. such
as some text retrieved from an XML file)? Thus if you pass it "1" or
"5.12345" it returns true, but "1b3q" it returns false? I know VB has this
sort of function, but was wondering how to implement something similar in
C#. I suppose I could use the Convert methods and catch any exceptions to
determine this, but is there a better technique?

TIA,
 
J

Jon Skeet [C# MVP]

Does anyone have a good/reliable approach to implementing an IsNumeric()
method that accepts a string that may represent a numerical value (eg. such
as some text retrieved from an XML file)? Thus if you pass it "1" or
"5.12345" it returns true, but "1b3q" it returns false? I know VB has this
sort of function, but was wondering how to implement something similar in
C#. I suppose I could use the Convert methods and catch any exceptions to
determine this, but is there a better technique?

Simplest way: try to convert and catch the exception
Next simplest way: use a regular expression to weed out some dodgy
values, then try to convert and catch the exception
Fastest way: write a hand-crafted routine to weed out some dodgy
values, then try to convert and catch the exception

It's actually not too difficult to write a routine to do this, but you
need to be careful of a few things:

o Do you want +5 to be okay?
o How about -5?
o How about whitespace?
o Don't forget that .5 is generally considered okay, so don't
require digits before dots
o What about 1,5 in some European countries? What culture are you
interested in?

Generally, you should rule things out cautiously - it's okay to allow
dodgy values as they'll be caught when you try the conversion, but it's
not okay to reject any valid values, so err on the side of caution.
 
J

John Bowman

Jon,

Thanks for the help. I've whipped together a simple routine that may mostly
work for my purposes. It probably doesn't work in ALL cases, but for the
type of data I'll be encountering, I think it'll work... except for 1 thing.
I can't get it to work w/ scientific notation. I've included the code below:

The following calls return true correctly:
IsNumeric("1");
IsNumeric("1.0");
IsNumeric("5.12345678910123456");
IsNumeric("123456789");
IsNumeric("-1.2345");
IsNumeric("1-34bq55.5678");

The following calls return false incorrectly:
IsNumeric("1.234+e5");
IsNumeric("-1.234+e-5");
IsNumeric("5+e2");


public bool IsNumeric(string Str)
{
bool bTmp = false;
try
{
long lTmp = 0L;
double dTmp = 0.0D;
float fTmp = 0F;
int nTmp = 0;

bool bLong = false;
bool bDouble = false;
bool bFloat = false;
bool bInt = false;

try
{
//Try a long conversion.
lTmp = Int64.Parse(Str);
bLong = true;
}
catch(Exception eLong)
{
eLong = eLong;
}

try
{
//Try a double conversion.
dTmp = Double.Parse(Str);
bDouble = true;
}
catch(Exception eDouble)
{
eDouble = eDouble;
}

try
{
//Try a float conversion.
fTmp = Single.Parse(Str);
bFloat = true;
}
catch(Exception eFloat)
{
eFloat = eFloat;
}

try
{
//Try an int conversion.
nTmp = Int32.Parse(Str);
nTmp = Int16.Parse(Str);
bInt = true;
}
catch(Exception eInt)
{
eInt = eInt;
}

//Were any conversions succesful?
if((bLong == true) ||
(bDouble == true) ||
(bFloat == true) ||
(bInt == true))
{
bTmp = true;
}

}
catch(Exception eAll)
{
//Catch everything that goes wrong and return false;
eAll = eAll;
}

return bTmp;
}


I also, tried using something like the following to get the scientific
notation to work:

dTmp = (double)Convert.ChangeType(Str, typeof(double));

but alas it didn't work either. Any ideas/thoughts?


TIA,

John
 
D

Daniel Pratt

Hi John,

John Bowman [email protected]> said:
Jon,

Thanks for the help. I've whipped together a simple routine that may mostly
work for my purposes. It probably doesn't work in ALL cases, but for the
type of data I'll be encountering, I think it'll work... except for 1 thing.
I can't get it to work w/ scientific notation. I've included the code
below:

Given this routine:

private static bool IsNumeric(string inputValue)
{
NumberStyles numerStyle =
NumberStyles.AllowExponent |
NumberStyles.AllowThousands |
NumberStyles.AllowDecimalPoint |
NumberStyles.AllowLeadingSign;

double outputValue;

return Double.TryParse(
inputValue,
numerStyle,
CultureInfo.InvariantCulture,
out outputValue);
}


The following inputs are recognized as numeric (notice the difference in
exponent notation from your inputs):

"1"
"1.0"
"5.12345678910123456"
"123456789"
"-1.2345"
"1.234e+5"
"-1.234e-5"
"5e+2"

The following are not recognized as numeric:

"1-34bq55.5678" (you said this was correctly identified as numeric,
but it's not a notation I am familiar with)
"1.234+e5" (seems to choke on the "+" between the base and exponent)
"-1.234+e-5" (same here)
"5+e2" (and here)

The other benefit to using Double.TryParse, besides being able to be
specific about what number formats you will accept, is that it won't throw
an exception, which should make it perform better. Hope this helps.

Regards,
Dan
 
J

John Bowman

Daniel,

Thanks for the additional insight & suggestion.
"1-34bq55.5678" (you said this was correctly identified as numeric,
but it's not a notation I am familiar with)

Oops, I put this one in the wrong catagory, it correctly returns false,
using my original code.. aka, it decided correctly.
"1.234+e5" (seems to choke on the "+" between the base and exponent)
"-1.234+e-5" (same here)
"5+e2" (and here)

I think I may have come up with a mechanism to handle the scientific
notation. I'm going to try something like the following.

Using a Regex.Split call atttempt to split the string into the base &
exponent parts.

If length of the split array == 2, then call IsNumeric() again, on the base
and on the exponent portions respectively to determine if they are both
numeric, if so then IsNumeric() should return true, else return false.

Else if split array length != 2, then it's not scientifc notation and the
other approach I already have worked out using the TryParse instead, should
be called to handle other possibilities.

Do you think that's reasonble?


TIA again,

John
 
D

Daniel Pratt

Hi John,

John Bowman [email protected]> said:
Daniel,

Thanks for the additional insight & suggestion.
I think I may have come up with a mechanism to handle the scientific
notation. I'm going to try something like the following.

Using a Regex.Split call atttempt to split the string into the base &
exponent parts.

If length of the split array == 2, then call IsNumeric() again, on the base
and on the exponent portions respectively to determine if they are both
numeric, if so then IsNumeric() should return true, else return false.

Else if split array length != 2, then it's not scientifc notation and the
other approach I already have worked out using the TryParse instead, should
be called to handle other possibilities.

Do you think that's reasonble?

Yes, I think that would be reasonable. Another (possibly simpler, safer)
approach would be to modify the routine I posted to replace any occurances
of "+e" with just "e":

private static bool IsNumeric(string inputValue)
{
NumberStyles numberStyle =
NumberStyles.AllowExponent |
NumberStyles.AllowThousands |
NumberStyles.AllowDecimalPoint |
NumberStyles.AllowLeadingSign;

double outputValue;

return Double.TryParse(
inputValue.Replace("+e", "e"), // note change here.
numberStyle,
CultureInfo.InvariantCulture,
out outputValue);
}

Regards,
Dan
 
J

Jon Skeet [C# MVP]

Thanks for the help. I've whipped together a simple routine that may mostly
work for my purposes. It probably doesn't work in ALL cases, but for the
type of data I'll be encountering, I think it'll work... except for 1 thing.
I can't get it to work w/ scientific notation. I've included the code below:

I'm not sure why you're trying for each of float, double, long and int.
Every int is also a long. Every float is also a double. I *suspect*
that every long is also parsed by double.Parse, even if the value
returned isn't the exact long - so just a single call to double.Parse
would give you the appropriate response.

However, to allow exponents you could use the version of Double.Parse
which takes a NumberStyles, and include the AllowExponent value.
I also, tried using something like the following to get the scientific
notation to work:

dTmp = (double)Convert.ChangeType(Str, typeof(double));

but alas it didn't work either. Any ideas/thoughts?

Can't say I've ever used that - but the code above should do you fine.
 
J

John Bowman

Daniel,
Yes, I think that would be reasonable. Another (possibly simpler, safer)
approach would be to modify the routine I posted to replace any occurances
of "+e" with just "e":

I was planning on incorporating that change ... <g>

Thanks again,


John
 
J

John Bowman

Jon,

Thanks for the additional info.
I'm not sure why you're trying for each of float, double, long and int.
Every int is also a long. Every float is also a double. I *suspect*
that every long is also parsed by double.Parse, even if the value
returned isn't the exact long - so just a single call to double.Parse
would give you the appropriate response.

I realize this, I only left them in there to help w/ the
debugging/illustration process ... didn't mean to confuse the issue.

However, to allow exponents you could use the version of Double.Parse
which takes a NumberStyles, and include the AllowExponent value.

This dovetails nicely w/ Daniel's suggestion... will do

Can't say I've ever used that - but the code above should do you fine.

Just trying out other ideas, trying to get it to work.

I'm suspecting I can get this to work OK now. Thanks to you both for he
help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top