[ACCEPTED]-Parse an integer from a string with trailing garbage-parsing

Accepted answer
Score: 25

You can use Linq to do this, no Regular 2 Expressions needed:

public static int GetLeadingInt(string input)
{
   return Int32.Parse(new string(input.Trim().TakeWhile(c => char.IsDigit(c) || c == '.').ToArray()));
}

This works for all your 1 provided examples:

string[] tests = new string[] {
   "1",
   " 42 ",
   " 3 -.X.-",
   " 2 3 4 5"
};

foreach (string test in tests)
{
   Console.WriteLine("Result: " + GetLeadingInt(test));
}
Score: 17
foreach (var m in Regex.Matches(" 3 - .x. 4", @"\d+"))
{
    Console.WriteLine(m);
}

Updated per comments

Not sure why you don't 3 like regular expressions, so I'll just post 2 what I think is the shortest solution.

To 1 get first int:

Match match = Regex.Match(" 3 - .x. - 4", @"\d+");
if (match.Success)
    Console.WriteLine(int.Parse(match.Value));
Score: 6

There's no standard .NET method for doing 10 this - although I wouldn't be surprised 9 to find that VB had something in the Microsoft.VisualBasic 8 assembly (which is shipped with .NET, so 7 it's not an issue to use it even from C#).

Will 6 the result always be non-negative (which 5 would make things easier)?

To be honest, regular 4 expressions are the easiest option here, but...

public static string RemoveCruftFromNumber(string text)
{
    int end = 0;

    // First move past leading spaces
    while (end < text.Length && text[end] == ' ')
    {
        end++;
    }

    // Now move past digits
    while (end < text.Length && char.IsDigit(text[end]))
    {
        end++;
    }

    return text.Substring(0, end);
}

Then 3 you just need to call int.TryParse on the result of 2 RemoveCruftFromNumber (don't forget that the integer may be too 1 big to store in an int).

Score: 5

I like @Donut's approach.

I'd like to add 6 though, that char.IsDigit and char.IsNumber also allow for some unicode 5 characters which are digits in other languages 4 and scripts (see here).
If you only want to check 3 for the digits 0 to 9 you could use "0123456789".Contains(c).

Three 2 example implementions:

To remove trailing non-digit characters:

var digits = new string(input.Trim().TakeWhile(c =>
    ("0123456789").Contains(c)
).ToArray());

To remove leading non-digit characters:

var digits = new string(input.Trim().SkipWhile(c =>
    !("0123456789").Contains(c)
).ToArray());

To remove all non-digit characters:

var digits = new string(input.Trim().Where(c =>
    ("0123456789").Contains(c)
).ToArray());

And of course: int.Parse(digits) or 1 int.TryParse(digits, out output)

Score: 1
string s = " 3 -.X.-".Trim();
string collectedNumber = string.empty;
int i;

for (x = 0; x < s.length; x++) 
{

  if (int.TryParse(s[x], out i))
     collectedNumber += s[x];
  else
     break;     // not a number - that's it - get out.

} 

if (int.TryParse(collectedNumber, out i))
    Console.WriteLine(i); 
else
    Console.WriteLine("no number found");

0

Score: 1

This is how I would have done it in Java:

int parseLeadingInt(String input)
{
    NumberFormat fmt = NumberFormat.getIntegerInstance();
    fmt.setGroupingUsed(false);
    return fmt.parse(input, new ParsePosition(0)).intValue();
}

I 3 was hoping something similar would be possible 2 in .NET.

This is the regex-based solution 1 I am currently using:

int? parseLeadingInt(string input)
{
    int result = 0;
    Match match = Regex.Match(input, "^[ \t]*\\d+");
    if (match.Success && int.TryParse(match.Value, out result))
    {
        return result;
    }
    return null;
}
Score: 1

This doesn't really answer your question 8 (about a built-in C# method), but you could 7 try chopping off characters at the end of 6 the input string one by one until int.TryParse() accepts 5 it as a valid number:

for (int p = input.Length;  p > 0;  p--)
{
    int  num;
    if (int.TryParse(input.Substring(0, p), out num))
        return num;
}
throw new Exception("Malformed integer: " + input);

Of course, this will 4 be slow if input is very long.

ADDENDUM (March 2016)

This could be 3 made faster by chopping off all non-digit/non-space 2 characters on the right before attempting 1 each parse:

for (int p = input.Length;  p > 0;  p--)
{
    char  ch;
    do
    {
        ch = input[--p];
    } while ((ch < '0'  ||  ch > '9')  &&  ch != ' '  &&  p > 0);
    p++;

    int  num;
    if (int.TryParse(input.Substring(0, p), out num))
        return num;
}
throw new Exception("Malformed integer: " + input);
Score: 0

I'm not sure why you would avoid Regex in 4 this situation.

Here's a little hackery 3 that you can adjust to your needs.

" 3 -.X.-".ToCharArray().FindInteger().ToList().ForEach(Console.WriteLine);

public static class CharArrayExtensions
{
    public static IEnumerable<char> FindInteger(this IEnumerable<char> array)
    {
        foreach (var c in array)
        {
            if(char.IsNumber(c))
                yield return c;
        }
    }
}

EDIT: That's 2 true about the incorrect result (and the 1 maintenance dev :) ).

Here's a revision:

    public static int FindFirstInteger(this IEnumerable<char> array)
    {
        bool foundInteger = false;
        var ints = new List<char>();

        foreach (var c in array)
        {
            if(char.IsNumber(c))
            {
                foundInteger = true;
                ints.Add(c);
            }
            else
            {
                if(foundInteger)
                {
                    break;
                }
            }
        }

        string s = string.Empty;
        ints.ForEach(i => s += i.ToString());
        return int.Parse(s);
    }
Score: 0
    private string GetInt(string s)
    {
        int i = 0;

        s = s.Trim();
        while (i<s.Length && char.IsDigit(s[i])) i++;

        return s.Substring(0, i);
    }

0

Score: 0

Might as well add mine too.

        string temp = " 3 .x£";
        string numbersOnly = String.Empty;
        int tempInt;
        for (int i = 0; i < temp.Length; i++)
        {
            if (Int32.TryParse(Convert.ToString(temp[i]), out tempInt))
            {
                numbersOnly += temp[i];
            }
        }

        Int32.TryParse(numbersOnly, out tempInt);
        MessageBox.Show(tempInt.ToString());

The message box 2 is just for testing purposes, just delete 1 it once you verify the method is working.

More Related questions