tags:

views:

331

answers:

9

In my limited experience, I've been on several projects that have had some sort of string utility class with methods to determine if a given string is a number. The idea has always been the same, however, the implementation has been different. Some surround a parse attempt with try/catch

public boolean isInteger(String str) {
    try {
        Integer.parseInt(str);
        return true;
    } catch (NumberFormatException nfe) {}
    return false;
}

and others match with regex

public boolean isInteger(String str) {
    return str.matches("^-?[0-9]+(\\.[0-9]+)?$");
}

Is one of these methods better than the other? I personally prefer using the regex approach, as it's concise, but will it perform on par if called while iterating over, say, a list of a several hundred thousand strings?

Note: As I'm kinda new to the site I don't fully understand this Community Wiki business, so if this belongs there let me know, and I'll gladly move it.

EDIT: With all the TryParse suggestions I ported Asaph's benchmark code (thanks for a great post!) to C# and added a TryParse method. And as it seems, the TryParse wins hands down. However, the try catch approach took a crazy amount of time. To the point of me thinking I did something wrong! I also updated regex to handle negatives and decimal points.

Results for updated, C# benchmark code:

00:00:51.7390000 for isIntegerParseInt
00:00:03.9110000 for isIntegerRegex
00:00:00.3500000 for isIntegerTryParse

Using:

static bool isIntegerParseInt(string str) {
    try {
        int.Parse(str);
        return true;
    } catch (FormatException e){}
    return false;
}

static bool isIntegerRegex(string str) {
    return Regex.Match(str, "^-?[0-9]+(\\.[0-9]+)?$").Success;
}

static bool isIntegerTryParse(string str) {
    int bob;
    return Int32.TryParse(str, out bob);
}
+1  A: 

Some languages, like C#, have a TryParse (or equivalent) that works fairly well for something like this.

public boolean IsInteger(string value)
{
  int i;
  return Int32.TryParse(value, i);
}
Brandon
+2  A: 

Personally I would do this if you really want to simplify it.

public boolean isInteger(string myValue)
{
    int myIntValue;
    return int.TryParse(myValue, myIntValue)
}
Mitchel Sellers
+1  A: 

If absolute performance is key, and if you are just checking for integers (not floating point numbers) I suspect that iterating over each character in the string, returning false if you encounter something not in the range 0-9, will be fastest.

RegEx is a more general-purpose solution so will probably not perform as fast for that special case. A solution that throws an exception will have some extra overhead in that case. TryParse will be slightly slower if you don't actually care about the value of the number, just whether or not it is a number, since the conversion to a number must also take place.

For anything but an inner loop that's called many times, the differences between all of these options should be insignificant.

Eric J.
+4  A: 

I just ran some benchmarks on the performance of these 2 methods (On Macbook Pro OSX Leopard Java 6). ParseInt is faster. Here is the output:

This operation took 1562 ms.
This operation took 2251 ms.

And here is my benchmark code:


public class IsIntegerPerformanceTest {

    public static boolean isIntegerParseInt(String str) {
        try {
            Integer.parseInt(str);
            return true;
        } catch (NumberFormatException nfe) {}
        return false;
    }

    public static boolean isIntegerRegex(String str) {
        return str.matches("^[0-9]+$");
    }

    public static void main(String[] args) {
     long starttime, endtime;
     int iterations = 1000000;
     starttime = System.currentTimeMillis();
     for (int i=0; i<iterations; i++) {
      isIntegerParseInt("123");
      isIntegerParseInt("not an int");
      isIntegerParseInt("-321");
     }
     endtime = System.currentTimeMillis();
     System.out.println("This operation took " + (endtime - starttime) + " ms.");
     starttime = System.currentTimeMillis();
     for (int i=0; i<iterations; i++) {
      isIntegerRegex("123");
      isIntegerRegex("not an int");
      isIntegerRegex("-321");
     }
     endtime = System.currentTimeMillis();
     System.out.println("This operation took " + (endtime - starttime) + " ms.");
    }
}

Also, note that your regex will reject negative numbers and the parseInt method will accept them.

Asaph
What about int.TryParse()? Can we assume that it is exactly the same in terms of performance as the first try/catch?
Dan Atkinson
Alas, this isn't C# code! lol!
Dan Atkinson
A: 

I use this but I liked Asaph's rigor in his post.

public static bool IsNumeric(object expression)
{
if (expression == null)
return false;

double number;
return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture),   NumberStyles.Any,
NumberFormatInfo.InvariantInfo, out number);
}
Daver
+3  A: 

Using .NET, you could do something like:

private bool isNumber(string str)
{
    return str.Any(c => !char.IsDigit(c));
}
Cleiton
+2  A: 

You could create an extension method for a string, and make the whole process look cleaner...

public static bool IsInt(this string str)
{
    int i;
    return int.TryParse(str, out i);
}

You could then do the following in your actual code...

if(myString.IsInt())....
RSolberg
+1  A: 

Here is our way of doing this:

public boolean isNumeric(String string) throws IllegalArgumentException
{
   boolean isnumeric = false;

   if (string != null && !string.equals(""))
   {
      isnumeric = true;
      char chars[] = string.toCharArray();

      for(int d = 0; d < chars.length; d++)
      {
         isnumeric &= Character.isDigit(chars[d]);

         if(!isnumeric)
         break;
      }
   }
   return isnumeric;
}
Jeremy Cron
A: 

Similar to a few others, but I like to accept thousand separators for my integers (unless I'm in tight control of the data passed). I'm not in a position to worry about other cultures. I use decimal.TryParse with NumberStyles.Currency for decimals/money parsing.

private bool isNumber(this string value)
{
   int oInt;
   if (!string.IsNullOrEmpty(value) && Int32.TryParse(value,
      System.Globalization.NumberStyles.Integer | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out oInt))            
    return true;
  return false;
}
benjynito