views:

873

answers:

6

I was wondering if anyone had put together something or had seen something equivalent to the JavaScript parseInt for C#.

Specifically, i'm looking to take a string like:

123abc4567890

and return only the first valid integer

123

I have a static method I've used that will return only the numbers:

public static int ParseInteger( object oItem )
 {
  string sItem = oItem.ToString();

  sItem = Regex.Replace( sItem, @"([^\d])*", "" );

  int iItem = 0;

  Int32.TryParse( sItem, out iItem );

  return iItem;
 }

The above would take:

ParseInteger( "123abc4567890" );

and give me back

1234567890

I'm not sure if it's possible to do with a regular expression, or if there is a better method to grab just the first integer from the string.

+5  A: 

You are close.

You probably just want:

foreach (var match in Regex.Matches(input, @"\d+"))
{
  return int.Parse(match.Value);
}
leppie
That will convert "ab12cd" to 12 won't it? That may well be the desired behaviour - I'm not sure. I'm only asking because my version only works if the number is at the start of the string.
Jon Skeet
Ahh, you are right, a ^ at the start of the RE qould probably be good enough :) Thanks
leppie
leppie: I wasn't suggesting that was required, so much as that your implementation's behaviour may well be desirable, and I wanted to check :)
Jon Skeet
You can't use var in that foreach because Regex.Matches returns a MatchCollection that is a non generic ICollection. As it happens I blogged about this yesterday: http://alicebobandmallory.com/articles/2009/06/09/no-var-for-me-in-the-foreach
Jonas Elfström
+7  A: 

Here's a complete example. It will throw an exception if you don't give it a valid string - you can change this behaviour by not explicitly throwing an exception in ParseInteger, and using int.TryParse instead.

Note that it allows a leading - sign as well, but not a leading +. (Again, easy to change.)

Also note that although I've got three test cases for success situations, I haven't got any test cases for failure.

Finally, it won't match "abc123def". If you want it to, remove the ^ from the regex.

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

class Test
{
    static void Main(string[] args)
    {
        Check("1234abc", 1234);
        Check("-12", -12);
        Check("123abc456", 123);
    }

    static void Check(string text, int expected)
    {
        int actual = ParseInteger(text);
        if (actual != expected)
        {
            Console.WriteLine("Expected {0}; got {1}", expected, actual);
        }
    }

    private static readonly Regex LeadingInteger = new Regex(@"^(-?\d+)");

    static int ParseInteger(string item)
    {
        Match match = LeadingInteger.Match(item);
        if (!match.Success)
        {
            throw new ArgumentException("Not an integer");
        }
        return int.Parse(match.Value);
    }
}
Jon Skeet
can you tell me, if there's any difference in the runtime/performance between yours, accepted and mine? does internally regex works exactly the way i posted?
Andreas Niedermair
You'd have to benchmark it to find out the performance. I don't know the details of how .NET regexes are implemented. It will depend on the options though - in this case I haven't specified that it should be compiled, which would probably help performance after the initial hit of compilation.
Jon Skeet
as you wrote a benchmark-suite: would you possibly run this test in some free time for me?
Andreas Niedermair
That benchmark suite is still very much under development, I'm afraid. With the revisions for C# in Depth for the second edition, I don't really have any spare time for this at the moment. Sorry :(
Jon Skeet
ah ... that's ok ... i'll have a look at it on my own, when i got time!
Andreas Niedermair
A: 

You could use this RegEx ("\A\d+") to find numbers at the beginning of a string.

You can then use int.Parse() to convert that string into an actual integer.

nikmd23
+1  A: 
    public static int ParseInteger( object oItem )
    {
            int iItem = 0;
            if (oItem != null) {
                    Int32.TryParse( Regex.Match( oItem.ToString(), @"\d+" ).Value, out iItem );
            }
            return iItem;
    }
Lucero
+2  A: 

Slight change to Jon Skeet's excellent solution (I don't have enough rep to comment).

I would change the regex to (as mentioned by Jon):

@"^([^\d]+)?(+|-)?\d+"

This allows for the case of leading characters ahead of the first occurrence of a digit (e.g., asb12354 -> 12354) and both signed integer cases (e.g., + or -)

Mark
A: 
public static int ParseInteger(string value)
{
    var result = default(int);
    if (string.IsNullOrEmpty(value))
    {
     return result;
    }
    var sb = new StringBuilder();
    foreach (var letter in value)
    {
     if ('0' <= letter
      && letter <= '9')
     {
      sb.Append(letter);
     }
    }

    // alternative
    /*
    var letter = default(char);
    for (var i = 0; i < value.Length; i++)
    {
     letter = value[i];
     if ('0' <= letter
      && letter <= '9')
     {
      sb.Append(letter);
     }
    }
    */

    int.TryParse(sb.ToString(), out result);

    return result;
}

I'm just posting for completion and: can someone tell me if there's a difference in the calls/runtimes for the accepted version (regex) and mine?

Andreas Niedermair