Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Checking if a string can be converted to Int32

147 views
Skip to first unread message

Peter Holpar

unread,
Jun 18, 2003, 3:33:33 AM6/18/03
to
Hi,

I'd like to do this job without having to use a try/catch block to catch the
exception when the conversion cannot be done.

I've tried this chunk of code:

bool bResult = false;
string sMyString = "weqww";
Int32Converter oConverter;
oConverter =
(Int32Converter)TypeDescriptor.GetConverter(typeof(System.Int32));
bResult = oConverter.IsValid(sMyString);

I found that bResult is always true, although the string value cannot be
converted to integer.
Any idea?

Thanks,
Peter


Stefan Simek

unread,
Jun 18, 2003, 9:43:10 AM6/18/03
to
Why do you want to do so?

I expect any code you could possibly write would be slower than using the
try/catch exception mechanism. Do you have any better reason why not to use
it?

Stefan

"Peter Holpar" <pho...@grepton.hu> wrote in message
news:eK0qLvWN...@TK2MSFTNGP12.phx.gbl...

Jon Skeet

unread,
Jun 18, 2003, 10:13:16 AM6/18/03
to
Stefan Simek <si...@kascomp.sk> wrote:
> Why do you want to do so?
>
> I expect any code you could possibly write would be slower than using the
> try/catch exception mechanism.

What makes you say that? The try/catch exception mechanism is very fast
when no exception is thrown, but not when exceptions *are* thrown. If
there are a lot of strings which won't be converted, a simple check
would help performance, potentially significantly. Here's a test
program:

using System;

public class Test
{
private static readonly string[] TestStrings =
{"-12345", "667890", "12345678901234567890",
"foo", "123456789-", "-0000000012345678910",
"00000000001234567891012345"};

const int Iterations = 100000;

public static void Main()
{
{
DateTime start = DateTime.Now;
long total=0;
for (int i=0; i < Iterations; i++)
{
for (int j=0; j < TestStrings.Length; j++)
{
try
{
total += Int32.Parse(TestStrings[j]);
}
catch
{
}
}
}
DateTime end = DateTime.Now;
Console.WriteLine ("No extra checking: {0}", end-start);
Console.WriteLine ("(Total={0})", total);
}

{
DateTime start = DateTime.Now;
long total=0;
for (int i=0; i < Iterations; i++)
{
for (int j=0; j < TestStrings.Length; j++)
{
try
{
string test = TestStrings[j];
if (PreliminaryCheck(test))
total += Int32.Parse(test);
}
catch
{
}
}
}
DateTime end = DateTime.Now;
Console.WriteLine ("With preliminary checking: {0}",
end-start);
Console.WriteLine ("(Total={0})", total);
}
}

private static readonly char[] TrimStartArray = {'0'};
private static bool PreliminaryCheck(string x)
{
// Check for being too short
if (x==null || x.Length==0)
return false;

// Check for being too long, remembering that 000000000000123,
// +00000000000000000123 and -000000000000123 are all valid.
if (x.Length > 10)
{
bool hasSign=false;
if (x[0]=='+' || x[0]=='-')
hasSign = true;

if (!hasSign || x.Length>11)
{
string tmp = hasSign ? x.Substring(1) : x;
if (tmp.TrimStart(TrimStartArray).Length > 10)
return false;
}
}

// Check each character
for (int i=0; i < x.Length; i++)
{
char c = x[i];
if ((c < '0' || c > '9') &&
((c!='-' && c!='+')|| i>0))
return false;
}
return true;
}
}

On my box, the version using the preliminary check is nearly 100 times
faster. Now, for almost all tasks, that won't actually be relevant -
but in the rare occasion where the exceptions *are* taking up a
significant proportion of the time, the above may well help. Note that
it doesn't catch *everything*, but a fair number of illegal numbers.
Also note that it only includes digits 0-9 - no Arabic digits.

--
Jon Skeet - <sk...@pobox.com>
http://www.pobox.com/~skeet/
If replying to the group, please do not mail me too

Stefan Simek

unread,
Jun 19, 2003, 7:46:52 AM6/19/03
to
Thanks Jon,
I didn't know the exception handling mechanism was so slow...
I admit that I never gave a thought to how the internal mechanism of the
exceptions works and possibly have never written a code where a serious
performance penalty would occur through the use of exception blocks... or at
least didn't notice it among other troubles - like the slow painting of
controls.... ;)

Stefan

"Jon Skeet" <sk...@pobox.com> wrote in message
news:MPG.195a83d3...@news.microsoft.com...

Allen Zaudtke

unread,
Jun 20, 2003, 8:26:24 AM6/20/03
to
What about Double.TryParse(...)?

Al

"Stefan Simek" <si...@kascomp.sk> wrote in message
news:OHOjR#ZNDHA...@TK2MSFTNGP11.phx.gbl...

Jon Skeet

unread,
Jun 20, 2003, 8:49:08 AM6/20/03
to
Allen Zaudtke <zaudtke...@yahoo.com> wrote:
> What about Double.TryParse(...)?

Using Double.TryParse in my little test framework, this ended up being
significantly slower than my preliminary check: after trying the parse,
you need to check whether it's in the bounds of Int32, in which case
you can just use the parsed value rather than calling Int32.Parse
again. Alternatively, if you don't quite trust that, you can weed out
values which are too high/low, and maybe those containing a decimal
point, and then call Int32.Parse again.

Either way, it's slower than using my preliminary check, although much
faster than just relying on the exception mechanism.

Peter Holpar

unread,
Jun 23, 2003, 3:17:27 AM6/23/03
to
Hi,

Thank you for the replies.
In the meantime I found a few more methods to do this job (including
Double.TryParse).
For example:

Regular expressions:

using System.Text.RegularExpressions;

public static bool IsNumeric(string sNumber)
{
Regex reg = new Regex(@"^\d+$");
return reg.Match(sNumber).Success;
}


Using Microsoft Visual Basic .NET Runtime reference in the C# project:

public static bool IsNumeric(object oNumber)
{
return Microsoft.VisualBasic.Information.IsNumeric(oNumber);
}


Using Double.TryParse method (one can use NumberStyles.Integer or
NumberStyles.HexNumber instead of NumberStyles.Float):

public static bool IsNumeric(string sNumber)
{
double resNum;
return(Double.TryParse(sNumber,
System.Globalization.NumberStyles.Float,
System.Globalization.NumberFormatInfo.CurrentInfo, out
resNum ));
}

I didn't test the performace of the different methods, but maybe Jon could
give us some interesting results from his test framework.

Regards,
Peter

"Allen Zaudtke" <zaudtke...@yahoo.com> wrote in message
news:O$ww#byNDH...@tk2msftngp13.phx.gbl...

Jon Skeet

unread,
Jun 23, 2003, 3:55:00 AM6/23/03
to
Peter Holpar <pho...@grepton.hu> wrote:
> Thank you for the replies.
> In the meantime I found a few more methods to do this job (including
> Double.TryParse).
> For example:
>
> Regular expressions:
>
> using System.Text.RegularExpressions;
>
> public static bool IsNumeric(string sNumber)
> {
> Regex reg = new Regex(@"^\d+$");
> return reg.Match(sNumber).Success;
> }

Regular expressions are slower than direct code for this kind of thing,
and the above doesn't allow the number to be prefixed with + or -.

> Using Microsoft Visual Basic .NET Runtime reference in the C# project:
>
> public static bool IsNumeric(object oNumber)
> {
> return Microsoft.VisualBasic.Information.IsNumeric(oNumber);
> }

I'm not familiar with that, but I suspect it would also include
floating point numbers, which would then cause exceptions to be
generated at the Int32.Parse stage.

> Using Double.TryParse method (one can use NumberStyles.Integer or
> NumberStyles.HexNumber instead of NumberStyles.Float):
>
> public static bool IsNumeric(string sNumber)
> {
> double resNum;
> return(Double.TryParse(sNumber,
> System.Globalization.NumberStyles.Float,
> System.Globalization.NumberFormatInfo.CurrentInfo, out
> resNum ));
> }

I've tried something like the above in another post, I believe - it
didn't quite cut the mustard, although it was much better than the
original.

> I didn't test the performace of the different methods, but maybe Jon could
> give us some interesting results from his test framework.

Certainly :) I'll come up with a big version of the benchmark for
another post...

Jon Skeet

unread,
Jun 23, 2003, 4:58:41 AM6/23/03
to
Jon Skeet <sk...@pobox.com> wrote:
> Certainly :) I'll come up with a big version of the benchmark for
> another post...

Right. The code is below the results, but you'll need a copy of my
little benchmarking framework in order to run it. (It just makes
developing this kind of thing a bit easier. It's still going through
some fine tuning, and I hope to put it up today at
http://www.pobox.com/~skeet/csharp/benchmark.html )

Results:
Benchmarking type StringToInt
JustException: 00:01:15.7989936
HardCodedCheck: 00:00:00.7010080
DoubleTryParse: 00:00:40.8387232
Regex: 00:00:43.0418912
IsNumeric: 00:01:06.9062064


Looks like my check still wins hands down :)

Code:
// See http://www.pobox.com/~skeet/csharp/benchmark.html
// for details of how to run this code

using System;
using System.Globalization;
using System.Text.RegularExpressions;

public class StringToInt


{
private static readonly string[] TestStrings =

{"-12345", // Valid (and negative)
"667890", // Valid
"+667890", // Valid (and explicitly positive)
"12345678901234567890", // Invalid - too big
"foo", // Hopelessly invalid
"123456789-", // Invalid due to '-' at the end
"-0000000012345678910", // Valid - just!
"00000000001234567891012345", // Invalid - too big
"1234.45" // Invalid - not an integer
};

static int iterations = 100000;

static long total; // Total from the last run
static long validTotal=0; // Calculated in Init

public static void Init(string[] args)
{
if (args != null && args.Length>0)
iterations = Int32.Parse (args[0]);

validTotal=0;


for (int j=0; j < TestStrings.Length; j++)
{
try
{

validTotal += Int32.Parse(TestStrings[j]);
}
catch
{
}
}
validTotal *= iterations;

}

public static void Check()
{
if (total != validTotal)
throw new Exception (String.Format(
"Invalid total: {0}. Correct total: {1}",
total, validTotal));
}

[Benchmark]
public static void JustException()
{
long tot=0;
for (int i=0; i < iterations; i++)


{
for (int j=0; j < TestStrings.Length; j++)
{
try
{

tot += Int32.Parse(TestStrings[j]);
}
catch
{
}
}
}
total = tot;
}

[Benchmark]
public static void HardCodedCheck()
{
long tot=0;
for (int i=0; i < iterations; i++)


{
for (int j=0; j < TestStrings.Length; j++)
{
try
{
string test = TestStrings[j];
if (PreliminaryCheck(test))

tot += Int32.Parse(test);
}
catch
{
}
}
}
total = tot;
}

[Benchmark]
public static void DoubleTryParse()
{
long tot=0;
double d;
for (int i=0; i < iterations; i++)


{
for (int j=0; j < TestStrings.Length; j++)
{
try
{
string test = TestStrings[j];

if (Double.TryParse (test,
NumberStyles.Integer,
NumberFormatInfo.CurrentInfo,
out d))
tot += Int32.Parse(test);
}
catch
{
}
}
}
total = tot;
}


private static Regex regex = new Regex (@"^[+-]?\d+$",
RegexOptions.Compiled);

[Benchmark]
public static void Regex()
{
long tot=0;
for (int i=0; i < iterations; i++)


{
for (int j=0; j < TestStrings.Length; j++)
{
try
{
string test = TestStrings[j];

if (regex.IsMatch (test))
tot += Int32.Parse(test);
}
catch
{
}
}
}
total = tot;
}

[Benchmark]
public static void IsNumeric()
{
long tot=0;
for (int i=0; i < iterations; i++)


{
for (int j=0; j < TestStrings.Length; j++)
{
try
{
string test = TestStrings[j];

if (Microsoft.VisualBasic.Information.IsNumeric
(test))
tot += Int32.Parse(test);
}
catch
{
}
}
}
total = tot;

}

private static readonly char[] TrimStartArray = {'0'};
private static bool PreliminaryCheck(string x)
{
// Check for being too short
if (x==null || x.Length==0)
return false;

// Check for being too long, remembering that 000000000000123,
// +00000000000000000123 and -000000000000123 are all valid.
if (x.Length > 10)
{
bool hasSign=false;
if (x[0]=='+' || x[0]=='-')
hasSign = true;

if (!hasSign || x.Length>11)
{
string tmp = hasSign ? x.Substring(1) : x;
if (tmp.TrimStart(TrimStartArray).Length > 10)
return false;
}
}

// Check each character
for (int i=0; i < x.Length; i++)
{
char c = x[i];
if ((c < '0' || c > '9') &&
((c!='-' && c!='+')|| i>0))
return false;
}
return true;
}
}

Peter Holpar

unread,
Jun 27, 2003, 4:10:51 AM6/27/03
to
Hi,

Thank you for the replies.
In the meantime I found a few more methods to do this job (including
Double.TryParse).
For example:

Regular expressions:

using System.Text.RegularExpressions;

public static bool IsNumeric(string sNumber)
{
Regex reg = new Regex(@"^\d+$");
return reg.Match(sNumber).Success;
}

Using Microsoft Visual Basic .NET Runtime reference in the C# project:

public static bool IsNumeric(object oNumber)
{
return Microsoft.VisualBasic.Information.IsNumeric(oNumber);
}

Using Double.TryParse method (one can use NumberStyles.Integer or
NumberStyles.HexNumber instead of NumberStyles.Float):

public static bool IsNumeric(string sNumber)
{
double resNum;
return(Double.TryParse(sNumber,
System.Globalization.NumberStyles.Float,
System.Globalization.NumberFormatInfo.CurrentInfo, out
resNum ));
}

I didn't test the performace of the different methods, but maybe Jon could


give us some interesting results from his test framework.

Regards,
Peter

"Allen Zaudtke" <zaudtke...@yahoo.com> wrote in message
news:O$ww#byNDH...@tk2msftngp13.phx.gbl...

0 new messages