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
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...
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
"Jon Skeet" <sk...@pobox.com> wrote in message
news:MPG.195a83d3...@news.microsoft.com...
Al
"Stefan Simek" <si...@kascomp.sk> wrote in message
news:OHOjR#ZNDHA...@TK2MSFTNGP11.phx.gbl...
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.
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...
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...
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;
}
}
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...