Разобрать целое число из строки с конечным мусором - PullRequest
11 голосов
/ 13 октября 2009

Мне нужно проанализировать десятичное целое число, которое появляется в начале строки.

Возможно, после десятичного числа есть конечный мусор. Это необходимо игнорировать (даже если оно содержит другие числа.)

, например

"1" => 1
" 42 " => 42
" 3 -.X.-" => 3
" 2 3 4 5" => 2

Есть ли встроенный метод в .NET Framework для этого?

int.TryParse() не подходит. Это позволяет завершающие пробелы, но не другие завершающие символы.

Было бы довольно легко реализовать это, но я бы предпочел использовать стандартный метод, если он существует.

Ответы [ 10 ]

22 голосов
/ 13 октября 2009

Для этого вы можете использовать Linq, регулярные выражения не требуются:

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

Это работает для всех предоставленных вами примеров:

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

foreach (string test in tests)
{
   Console.WriteLine("Result: " + GetLeadingInt(test));
}
17 голосов
/ 13 октября 2009
foreach (var m in Regex.Matches(" 3 - .x. 4", @"\d+"))
{
    Console.WriteLine(m);
}

Обновлено по комментариям

Не знаю, почему вам не нравятся регулярные выражения, поэтому я просто опубликую то, что я считаю самым коротким решением.

Чтобы получить первый int:

Match match = Regex.Match(" 3 - .x. - 4", @"\d+");
if (match.Success)
    Console.WriteLine(int.Parse(match.Value));
5 голосов
/ 13 октября 2009

Нет стандартного метода .NET для этого - хотя я не удивлюсь, обнаружив, что VB имел что-то в сборке Microsoft.VisualBasic (которая поставляется с .NET, поэтому использовать его даже из C #).

Будет ли результат всегда неотрицательным (что облегчит задачу)?

Если честно, регулярные выражения - самый простой вариант, но ...

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);
}

Тогда вам просто нужно вызвать int.TryParse по результату RemoveCruftFromNumber (не забывайте, что целое число может быть слишком большим для хранения в int).

2 голосов
/ 23 августа 2013

Мне нравится подход @ Donut.

Хотелось бы добавить, что char.IsDigit и char.IsNumber также допускают некоторые символы Юникода, которые являются цифрами в других языках и сценариях ( см. Здесь ).
Если вы хотите проверить только цифры от 0 до 9, вы можете использовать "0123456789".Contains(c).

Три примера реализации:

Чтобы удалить завершающие нецифровые символы:

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

Для удаления начальных нецифровых символов:

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

Чтобы удалить все нецифровые символы:

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

И конечно: int.Parse(digits) или int.TryParse(digits, out output)

1 голос
/ 24 августа 2013

Это на самом деле не отвечает на ваш вопрос (о встроенном методе C #), но вы можете попробовать отрубить символы в конце входной строки один за другим, пока int.TryParse() не примет его в качестве действительного числа: 1002 *

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);

Конечно, это будет медленно, если input очень долго.

ADDENDUM (март 2016 г.)

Это можно сделать быстрее, отрубив все нецифровые / непробельные символы справа перед попыткой каждого разбора:

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);
1 голос
/ 13 октября 2009

Вот как я сделал бы это на Java:

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

Я надеялся, что нечто подобное будет возможно в .NET.

Это решение на основе регулярных выражений, которое я сейчас использую:

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;
}
1 голос
/ 13 октября 2009
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 голосов
/ 13 октября 2009

Можно добавить и мою тоже.

        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());

Окно сообщения только для целей тестирования, просто удалите его, как только убедитесь, что метод работает.

0 голосов
/ 13 октября 2009
    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 голосов
/ 13 октября 2009

Я не уверен, почему бы вам избежать Regex в этой ситуации.

Вот небольшой хакер, который вы можете настроить в соответствии со своими потребностями.

"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: Это правда о неправильном результате (и об уровне обслуживания :)).

Вот ревизия:

    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);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...