C#中StringToInt函数的最佳解决方案

时间:2010-05-31 12:27:02

标签: c#

我上周在求职面试中被要求在白板上做一个StringToInt / Int.parse功能但效果不是很好,但我提出了某种解决方案。后来回到家时,我在Visual Studion中做了一个,我想知道是否有比下面更好的解决方案。

除了检查字符串只包含数字外,还没有更多的错误处理。

        private int StrToInt(string tmpString)
    {
        int tmpResult = 0;

        System.Text.Encoding ascii = System.Text.Encoding.ASCII;
        byte[] tmpByte = ascii.GetBytes(tmpString);

        for (int i = 0; i <= tmpString.Length-1; i++)
        {
            // Check whatever the Character is an valid digit
            if (tmpByte[i] > 47 && tmpByte[i] <= 58)
                // Here I'm using the lenght-1 of the string to set the power and multiply this to the value
                tmpResult += (tmpByte[i] - 48) * ((int)Math.Pow(10, (tmpString.Length-i)-1));
            else
                throw new Exception("Non valid character in string");

        } 

        return tmpResult;
    }

7 个答案:

答案 0 :(得分:16)

我会采取逆向的方式。

public int? ToInt(this string mightBeInt)
{
    int convertedInt;
    if (int.TryParse(mightBeInt, out convertedInt))
    {
        return convertedInt;
    }
    return null;
}

在被告知这不是问题的关键之后,我认为问题是测试C编码技能,而不是C#。我进一步认为将字符串作为字符数组处理在.NET中是一个非常糟糕的习惯,因为字符串是unicode,并且在任何可能全球化的应用程序中,任何关于字符表示的假设都会让你遇到麻烦,更快或以后。此外,该框架已经提供了一种转换方法,它将比开发人员在如此匆忙中抛弃的任何东西更有效和可靠。重新发明框架功能总是一个坏主意。

然后我要指出,通过编写扩展方法,我已经为字符串类创建了一个非常有用的扩展,我将在生产代码中实际使用它。

如果那个论点让我失去了工作,我可能也不想在那里工作。

编辑:有几个人指出,我错过了TryParse中的“out”关键字。固定的。

答案 1 :(得分:8)

转换为字节数组是不必要的,因为字符串已经是char的数组。此外,应避免使用诸如48之类的幻数,以支持'0'等可读常量。我就是这样做的:

int result = 0;
for (int i = str.Length - 1, factor = 1; i >= 0; i--, factor *= 10)
    result += (str[i] - '0') * factor;

对于每个字符(从结尾开始),将其数值乘以10的正确幂10到结果。 10的幂是通过重复乘以10来计算的,而不是不必要地使用Math.Pow

答案 2 :(得分:5)

我认为你的解决方案相当合适,但不是做math.pow,我会这样做:

tmpResult = 10 * tmpResult + (tmpByte[i] - 48);

另外,检查长度与tmpByte的长度而不是tmpString。并不是说它通常很重要,但是在检查另一个数组的长度时循环遍历一个数组是相当奇怪的。

并且,您可以使用foreach语句替换for循环。

答案 3 :(得分:4)

如果你想要一个简单的非框架使用实现,那么该如何:

"1234".Aggregate(0, (s,c)=>  c-'0'+10*s)

...并注意在使用此方法之前,最好确保字符串仅由十进制数字组成。

或者,使用int?作为聚合值来处理错误处理:

"12x34".Aggregate((int?)0, (s,c)=>  c>='0'&&c<='9' ? c-'0'+10*s : null)

...这次注意到空字符串评估为0,这可能不是最合适的行为 - 并且不支持范围检查或负数;这两个都不难添加但需要看起来不太好的罗嗦的代码: - )。

显然,在实践中你只需要使用内置的解析方法。我实际上在实际项目中使用了以下扩展方法和一堆几乎相同的兄弟姐妹:

public static int? ParseAsInt32(this string s, NumberStyles style, IFormatProvider provider) {
    int val;
    if (int.TryParse(s, style, provider, out val)) return val;
    else return null;
}

虽然使用三元? :运算符可以稍微表达一下,但这意味着依赖于表达式中的副作用,这对我的经验中的可读性不是一个好处。

答案 4 :(得分:2)

因为我喜欢Linq:

string t = "1234";
var result = t.Select((c, i) => (c - '0') * Math.Pow(10, t.Length - i - 1)).Sum();

答案 5 :(得分:1)

我同意Cyclon Cat,他们可能希望有人利用现有的功能。 但我会把方法写得有点不同。

public int? ToInt(this string mightBeInt)
{
    int number = 0;
    if (Int32.TryParse(mightBeInt, out number))
        return number;
    return null;
}

Int32.TryParse不允许将属性作为out参数给出。

答案 6 :(得分:1)

我在接受采访时被问到这个问题超过9000次:)这个版本能够处理负数并能很好地处理其他条件:

public static int ToInt(string s)
{
    bool isNegative = false, gotAnyDigit = false;
    int result = 0;

    foreach (var ch in s ?? "")
    {
        if(ch == '-' && !(gotAnyDigit || isNegative))
        {
            isNegative = true;
        }
        else if(char.IsDigit(ch))
        {
            result = result*10 + (ch - '0');
            gotAnyDigit = true;
        }
        else
        {
            throw new ArgumentException("Not a number");
        }
    }

    if (!gotAnyDigit)
        throw new ArgumentException("Not a number");

    return isNegative ? -result : result;
}

和几个懒惰的测试:

    [TestFixture]
public class Tests
{
    [Test]
    public void CommonCases()
    {
        foreach (var sample in new[]
            {
                new {e = 123, s = "123"},
                new {e = 110, s = "000110"},
                new {e = -011000, s = "-011000"},
                new {e = 0, s = "0"},
                new {e = 1, s = "1"},
                new {e = -2, s = "-2"},
                new {e = -12223, s = "-12223"},
                new {e = int.MaxValue, s = int.MaxValue.ToString()},
                new {e = int.MinValue, s = int.MinValue.ToString()}
            })
        {
            Assert.AreEqual(sample.e, Impl.ToInt(sample.s));
        }
    }
    [Test]
    public void BadCases()
    {
        var samples = new[] { "1231a", null, "", "a", "-a", "-", "12-23", "--1" };
        var errCount = 0;

        foreach (var sample in samples)
        {
            try
            {
                Impl.ToInt(sample);
            }
            catch(ArgumentException)
            {
                errCount++;
            }
        }

        Assert.AreEqual(samples.Length, errCount);
    }
}