Як насправді працює int.TryParse


80

Я шукав int.TryParseреалізацію методу, як це працює насправді, але не знайшов. Я повинен знати, про a string, чи це числове значення, але наразі я не хочу його перетворювати.

Тому мені потрібен лише boolрезультат від int.TryParse. Отже, запитання такі:

  1. Чи існує якась функція, яка може надати лише boolрезультат,

і

  1. Я хотів би знати, як int.TryParseнасправді працює (чи є try ... catchвсередині або перебирається символи введення string)?

1. Ні, немає. Ви можете використовувати Convert.ToInt32(), але це призведе до винятку, якщо рядок неможливо проаналізувати. 2. Йому доведеться перебирати рядок, але він, мабуть, не вловить жодного винятку, оскільки це швидше, ніж ловити виняток зConvert.ToInt32()
Nolonar

Ви можете використовувати регулярний вираз ^ \ d + $. Або оцініть кожен символ для IsDigit.
папарацці

Відповіді:


103

Якщо вам потрібен лише boolрезультат, просто використовуйте повернене значення та ігноруйте outпараметр.

bool successfullyParsed = int.TryParse(str, out ignoreMe);
if (successfullyParsed){
    // ...
}

Редагувати : Тим часом ви також можете подивитися оригінальний вихідний код :

System.Int32.TryParse


Якщо я хочу знати, як щось насправді реалізовано, я використовую ILSpyдля декомпіляції .NET-коду.

Ось результат:

// int
/// <summary>Converts the string representation of a number to its 32-bit signed integer equivalent. A return value indicates whether the operation succeeded.</summary>
/// <returns>true if s was converted successfully; otherwise, false.</returns>
/// <param name="s">A string containing a number to convert. </param>
/// <param name="result">When this method returns, contains the 32-bit signed integer value equivalent to the number contained in s, if the conversion succeeded, or zero if the conversion failed. The conversion fails if the s parameter is null, is not of the correct format, or represents a number less than <see cref="F:System.Int32.MinValue"></see> or greater than <see cref="F:System.Int32.MaxValue"></see>. This parameter is passed uninitialized. </param>
/// <filterpriority>1</filterpriority>
public static bool TryParse(string s, out int result)
{
    return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}


// System.Number
internal unsafe static bool TryParseInt32(string s, NumberStyles style, NumberFormatInfo info, out int result)
{
    byte* stackBuffer = stackalloc byte[1 * 114 / 1];
    Number.NumberBuffer numberBuffer = new Number.NumberBuffer(stackBuffer);
    result = 0;
    if (!Number.TryStringToNumber(s, style, ref numberBuffer, info, false))
    {
        return false;
    }
    if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
    {
        if (!Number.HexNumberToInt32(ref numberBuffer, ref result))
        {
            return false;
        }
    }
    else
    {
        if (!Number.NumberToInt32(ref numberBuffer, ref result))
        {
            return false;
        }
    }
    return true;
}

І ні, я не бачу жодної Try-Catchsдороги:

// System.Number
private unsafe static bool TryStringToNumber(string str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
{
    if (str == null)
    {
        return false;
    }
    fixed (char* ptr = str)
    {
        char* ptr2 = ptr;
        if (!Number.ParseNumber(ref ptr2, options, ref number, numfmt, parseDecimal) || ((ptr2 - ptr / 2) / 2 < str.Length && !Number.TrailingZeros(str, (ptr2 - ptr / 2) / 2)))
        {
            return false;
        }
    }
    return true;
}

// System.Number
private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
{
    number.scale = 0;
    number.sign = false;
    string text = null;
    string text2 = null;
    string str2 = null;
    string str3 = null;
    bool flag = false;
    string str4;
    string str5;
    if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None)
    {
        text = numfmt.CurrencySymbol;
        if (numfmt.ansiCurrencySymbol != null)
        {
            text2 = numfmt.ansiCurrencySymbol;
        }
        str2 = numfmt.NumberDecimalSeparator;
        str3 = numfmt.NumberGroupSeparator;
        str4 = numfmt.CurrencyDecimalSeparator;
        str5 = numfmt.CurrencyGroupSeparator;
        flag = true;
    }
    else
    {
        str4 = numfmt.NumberDecimalSeparator;
        str5 = numfmt.NumberGroupSeparator;
    }
    int num = 0;
    char* ptr = str;
    char c = *ptr;
    while (true)
    {
        if (!Number.IsWhite(c) || (options & NumberStyles.AllowLeadingWhite) == NumberStyles.None || ((num & 1) != 0 && ((num & 1) == 0 || ((num & 32) == 0 && numfmt.numberNegativePattern != 2))))
        {
            bool flag2;
            char* ptr2;
            if ((flag2 = ((options & NumberStyles.AllowLeadingSign) != NumberStyles.None && (num & 1) == 0)) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
            {
                num |= 1;
                ptr = ptr2 - (IntPtr)2 / 2;
            }
            else
            {
                if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
                {
                    num |= 1;
                    number.sign = true;
                    ptr = ptr2 - (IntPtr)2 / 2;
                }
                else
                {
                    if (c == '(' && (options & NumberStyles.AllowParentheses) != NumberStyles.None && (num & 1) == 0)
                    {
                        num |= 3;
                        number.sign = true;
                    }
                    else
                    {
                        if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
                        {
                            break;
                        }
                        num |= 32;
                        text = null;
                        text2 = null;
                        ptr = ptr2 - (IntPtr)2 / 2;
                    }
                }
            }
        }
        c = *(ptr += (IntPtr)2 / 2);
    }
    int num2 = 0;
    int num3 = 0;
    while (true)
    {
        if ((c >= '0' && c <= '9') || ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))))
        {
            num |= 4;
            if (c != '0' || (num & 8) != 0)
            {
                if (num2 < 50)
                {
                    number.digits[(IntPtr)(num2++)] = c;
                    if (c != '0' || parseDecimal)
                    {
                        num3 = num2;
                    }
                }
                if ((num & 16) == 0)
                {
                    number.scale++;
                }
                num |= 8;
            }
            else
            {
                if ((num & 16) != 0)
                {
                    number.scale--;
                }
            }
        }
        else
        {
            char* ptr2;
            if ((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None && (num & 16) == 0 && ((ptr2 = Number.MatchChars(ptr, str4)) != null || (flag && (num & 32) == 0 && (ptr2 = Number.MatchChars(ptr, str2)) != null)))
            {
                num |= 16;
                ptr = ptr2 - (IntPtr)2 / 2;
            }
            else
            {
                if ((options & NumberStyles.AllowThousands) == NumberStyles.None || (num & 4) == 0 || (num & 16) != 0 || ((ptr2 = Number.MatchChars(ptr, str5)) == null && (!flag || (num & 32) != 0 || (ptr2 = Number.MatchChars(ptr, str3)) == null)))
                {
                    break;
                }
                ptr = ptr2 - (IntPtr)2 / 2;
            }
        }
        c = *(ptr += (IntPtr)2 / 2);
    }
    bool flag3 = false;
    number.precision = num3;
    number.digits[(IntPtr)num3] = '\0';
    if ((num & 4) != 0)
    {
        if ((c == 'E' || c == 'e') && (options & NumberStyles.AllowExponent) != NumberStyles.None)
        {
            char* ptr3 = ptr;
            c = *(ptr += (IntPtr)2 / 2);
            char* ptr2;
            if ((ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
            {
                c = *(ptr = ptr2);
            }
            else
            {
                if ((ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
                {
                    c = *(ptr = ptr2);
                    flag3 = true;
                }
            }
            if (c >= '0' && c <= '9')
            {
                int num4 = 0;
                do
                {
                    num4 = num4 * 10 + (int)(c - '0');
                    c = *(ptr += (IntPtr)2 / 2);
                    if (num4 > 1000)
                    {
                        num4 = 9999;
                        while (c >= '0' && c <= '9')
                        {
                            c = *(ptr += (IntPtr)2 / 2);
                        }
                    }
                }
                while (c >= '0' && c <= '9');
                if (flag3)
                {
                    num4 = -num4;
                }
                number.scale += num4;
            }
            else
            {
                ptr = ptr3;
                c = *ptr;
            }
        }
        while (true)
        {
            if (!Number.IsWhite(c) || (options & NumberStyles.AllowTrailingWhite) == NumberStyles.None)
            {
                bool flag2;
                char* ptr2;
                if ((flag2 = ((options & NumberStyles.AllowTrailingSign) != NumberStyles.None && (num & 1) == 0)) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
                {
                    num |= 1;
                    ptr = ptr2 - (IntPtr)2 / 2;
                }
                else
                {
                    if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
                    {
                        num |= 1;
                        number.sign = true;
                        ptr = ptr2 - (IntPtr)2 / 2;
                    }
                    else
                    {
                        if (c == ')' && (num & 2) != 0)
                        {
                            num &= -3;
                        }
                        else
                        {
                            if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
                            {
                                break;
                            }
                            text = null;
                            text2 = null;
                            ptr = ptr2 - (IntPtr)2 / 2;
                        }
                    }
                }
            }
            c = *(ptr += (IntPtr)2 / 2);
        }
        if ((num & 2) == 0)
        {
            if ((num & 8) == 0)
            {
                if (!parseDecimal)
                {
                    number.scale = 0;
                }
                if ((num & 16) == 0)
                {
                    number.sign = false;
                }
            }
            str = ptr;
            return true;
        }
    }
    str = ptr;
    return false;
}

4
Це складніше, ніж я навчив :) Дякую!
speti43

19

Те, що int.TryParseнадає вам значення, не означає, що вам потрібно його зберегти; Ви можете з радістю зробити це:

int temp;
if (int.TryParse(inputString, out temp))
{
    // do stuff
}

Ви можете tempповністю ігнорувати, якщо це вам не потрібно. Якщо вам це потрібно, то привіт, це чекає вас, коли ви цього захочете.

Що стосується внутрішніх елементів, наскільки я пам’ятаю, він намагається прочитати необроблені байти рядка як int і перевіряє, чи є результат дійсним, чи щось інше; це не так просто, як ітерація шляхом пошуку нечислових символів.


16

Тепер ми можемо написати на C # 7.0 і вище:

if (int.TryParse(inputString, out _))
{
    //do stuff
}

Це не для відповіді на питання.
themefield

1
що робить out?
мід

@mrid Вказує, що змінна в області виклику надається області методу з доступом до запису. Наприклад, якщо змінну int someIntпередано int.TryParseвподобання int.TryParse(out someInt), вона int.TryParseможе розмістити її вихідні дані someInt, навіть якщо вона зазвичай виходить за рамки.
timelmer

@themefield - він відповідає на це питання прямо в тілі допису: "Отже, запитання такі: 1) Чи існує якась функція, яка може надати лише результат bool".
antiduh

13

TryParse - найкращий спосіб розбору або перевірки в одному рядку:

int nNumber = int.TryParse("InputString", out nNumber) ? nNumber : 1;

Короткий опис:

  1. nNumber ініціалізується нулем,
  2. int.TryParse () спробуйте проаналізувати "InputString" і перевірити його, якщо успіх встановити в nNumber.
  3. short if?: перевірка результату int.TryParse (), який повертає nNumber або 1 як значення за замовчуванням.

1
cannot use local variable 'nCurPage' before it is declared
Девін Берк

шановний Девін-Берк, ви праві, але це працює. Я використовую його неодноразово, без помилок і попереджень ...
Zolfaghari,

8

Regex компілюється так, щоб на швидкість створити його один раз і використати повторно.
Нове займає більше часу, ніж IsMatch.
Це перевіряє лише всі цифри.
Він не перевіряє діапазон.
Якщо вам потрібно протестувати діапазон, тоді TryParse - це шлях.

private static Regex regexInt = new Regex("^\\d+$");
static bool CheckReg(string value)
{
    return regexInt.IsMatch(value);
}

2

Перевірте цю просту програму, щоб зрозуміти int.TryParse

 class Program
 {
    static void Main()
    {
        string str = "7788";
        int num1;
        bool n = int.TryParse(str, out num1);
        Console.WriteLine(num1);
        Console.ReadLine();
    }
}

Вихід: 7788

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.