C# C语言中StringToInt函数的最佳解#
在上周的一次工作面试中,我被要求在白板上执行StringToInt/Int.parse函数,虽然效果不太好,但我还是想出了一些解决方案。后来回到家里,我在视觉研究中做了一个,我想知道是否有比下面我的更好的解决方案 除了检查字符串是否只包含数字外,没有进行任何其他错误处理C# C语言中StringToInt函数的最佳解#,c#,C#,在上周的一次工作面试中,我被要求在白板上执行StringToInt/Int.parse函数,虽然效果不太好,但我还是想出了一些解决方案。后来回到家里,我在视觉研究中做了一个,我想知道是否有比下面我的更好的解决方案 除了检查字符串是否只包含数字外,没有进行任何其他错误处理 private int StrToInt(string tmpString) { int tmpResult = 0; System.Text.Encoding asci
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;
}
private int strotint(字符串tmpString)
{
int-tmpResult=0;
System.Text.Encoding ascii=System.Text.Encoding.ascii;
字节[]tmpByte=ascii.GetBytes(tmpString);
对于(int i=0;i 47&&tmpByte[i]我认为您的解决方案是合理的,但我不做math.pow,而是做:
tmpResult = 10 * tmpResult + (tmpByte[i] - 48);
另外,根据tmpByte的长度而不是tmpString来检查长度。这通常并不重要,但在检查另一个数组的长度时循环一个数组是很奇怪的
而且,您可以用foreach语句替换for循环。转换为字节数组是不必要的,因为字符串已经是char
s的数组。此外,应该避免使用像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来计算的,而不是不必要地使用Math。Pow
我将采用相反的方法
public int? ToInt(this string mightBeInt)
{
int convertedInt;
if (int.TryParse(mightBeInt, out convertedInt))
{
return convertedInt;
}
return null;
}
在被告知这不是问题的重点之后,我认为这个问题测试的是C编码技能,而不是C#。我进一步认为,在.NET中,将字符串作为字符数组来处理是一个非常坏的习惯,因为字符串是unicode,在任何可能全球化的应用程序中,对字符表示做出任何假设tions迟早会给您带来麻烦。此外,框架已经提供了一种转换方法,它将比开发人员在如此匆忙中扔掉的任何东西都更加高效和可靠。重新发明框架功能总是一个坏主意
然后我会指出,通过编写一个扩展方法,我已经为string类创建了一个非常有用的扩展,我将在生产代码中实际使用它
如果那场争论让我失去了工作,我可能无论如何都不想在那里工作
编辑:正如一些人指出的,我错过了TryParse.Fixed中的“out”关键字。我同意Cyclon Cat,他们可能希望有人能够利用现有的功能。
但是我会写一些不同的方法
public int? ToInt(this string mightBeInt)
{
int number = 0;
if (Int32.TryParse(mightBeInt, out number))
return number;
return null;
}
Int32.TryParse不允许将属性作为out参数提供。仅仅因为我喜欢Linq:
string t = "1234";
var result = t.Select((c, i) => (c - '0') * Math.Pow(10, t.Length - i - 1)).Sum();
如果您想要一个使用实现的简单非框架,那么:
"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)
虽然这可以用三元的?:
操作符表达得稍微短一些,但这样做意味着依赖于表达式中的副作用,这对我的可读性并没有好处。我在采访中被问了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);
}
}
+1.诀窍确实是向后迭代字符。+1,我想我更喜欢这里面的逻辑,因为它不使用额外的变量(myfactor
)然后以自然的顺序循环字符串。非常好的帖子,我希望我能投两次票。很高兴看到像你这样的人跳出框框思考;你提出了几个非常有效的观点。就我个人而言,我认为测试候选人的基本逻辑/数学思维技能不是一个坏主意。当然,他们可以用一个问题在框架中没有内置的方法,但这仍然是一个很好的问题。我知道我在几年前实现我的第一个atoi
时,我必须思考几秒钟(顺便说一句,atoi
,是这个的C标准函数。我不明白你所说的“C编码技能”是什么意思)为什么不直接从if内部返回null?@Matti(继续)我同意基本的编码问题对于面试来说是公平的,但我更愿意看到面试官使用正确使用.NET的问题,而不是像这一个那样切中要害。我已经冒昧地修改了你的代码示例,以便它编译并做它应该做的事情-但我真的认为你应该请澄清或删除有关unicode和字符数组的误导性(或至少措辞拙劣)观点。感谢您的更正;我当时正在从头开始编写代码,但错过了“out”关键字,以及属性问题。我开始使用.net framwork和Int.Parse,但他们告诉我这基本上就是他们想要我生成的函数。这项工作是作为测试工程师在Microsoft完成的,我才知道我没有得到这项工作:)这项工作几乎符合以下条件:)需要一点时间才能习惯,但这就是S是lambda经常看起来的样子——如果你在VisualStudio中编写代码,它会自动插入人类友好的间距——我绝对崇拜的一个特性,希望它也可以是C++模式…实际上,我仍然在等待一个实用的IDE,它比语法高亮更进一步,包括内联字体和样式更改。更好地说明表达式的语义-例如,不强调没有语义意义的方括号和大括号,而是强调那些影响求值顺序的方括号和大括号;强制语义相关缩进,使复合标记如=>
或=
看起来
[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);
}
}