如何将Int转换为C#中的string而不使用ToString()?
将下面的int参数转换为一个string,而不使用任何本地的toStringfunction。
public string integerToString(int integerPassedIn){ //Your code here }
因为一切都从Object
inheritance,而Object
有一个ToString()
方法,所以如何将一个int
转换为一个string
而不使用原生的ToString()
方法?
string连接的问题是,它会调用链上的ToString()
直到它碰到一个或点击Object
类。
如何在不使用ToString()
情况下将整数转换为C#中的string?
像这样的东西:
public string IntToString(int a) { var chars = new[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; var str = string.Empty; if (a == 0) { str = chars[0]; } else if (a == int.MinValue) { str = "-2147483648"; } else { bool isNegative = (a < 0); if (isNegative) { a = -a; } while (a > 0) { str = chars[a % 10] + str; a /= 10; } if (isNegative) { str = "-" + str; } } return str; }
更新:这是另一个更短的版本,应该更好,因为它消除了所有string连接,有利于操纵一个固定长度的数组。 它支持多达16个的基地,但是很容易将它扩展到更高的基地。 它可能会进一步改善:
public string IntToString(int a, int radix) { var chars = "0123456789ABCDEF".ToCharArray(); var str = new char[32]; // maximum number of chars in any base var i = str.Length; bool isNegative = (a < 0); if (a <= 0) // handles 0 and int.MinValue special cases { str[--i] = chars[-(a % radix)]; a = -(a / radix); } while (a != 0) { str[--i] = chars[a % radix]; a /= radix; } if (isNegative) { str[--i] = '-'; } return new string(str, i, str.Length - i); }
这是我总是使用的解决scheme:
public static string numberBaseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static string IntToStringWithBase(int n, int b) { return IntToStringWithBase(n, b, 1); } public static string IntToStringWithBase(int n, int b, int minDigits) { if (minDigits < 1) minDigits = 1; if (n == 0) return new string('0', minDigits); string s = ""; if ((b < 2) || (b > numberBaseChars.Length)) return s; bool neg = false; if ((b == 10) && (n < 0)) { neg = true; n = -n; } uint N = (uint)n; uint B = (uint)b; while ((N > 0) | (minDigits-- > 0)) { s = numberBaseChars[(int)(N % B)] + s; N /= B; } if (neg) s = "-" + s; return s; }
这看起来相当复杂,但具有以下特点:
- 支持2到36的基数
- 处理负值
- 可选的总位数
我不是真的相信连接operator +
调用ToString
,但如果确实如此,可以通过执行以下操作来避免这两个操作:
if (a == 0) return "0"; /* Negative maxint doesn't have a corresponding positive value, so handle it * as a special case. Thanks to @Daniel for pointing this out. */ if (a == 0x80000000) return "-2147483648"; List<char> l = new List<char>(); bool negative = false; if (a < 0) { negative = true; a *= -1; } while (a > 0) { l.Add('0' + (char)(a % 10)); a /= 10; } if (negative) l.Add('-'); l.Reverse(); return new String(l.ToArray());
整数从最低有效位数字处理到最高有效位数。 计算一个数字,使用模10(%10),然后将其添加到字符值“0”。 这导致字符'0','1',…,'9'中的一个。
数字被压入堆栈,因为它们在处理时必须以相反的顺序呈现(最高有效位至最低有效位)。 这样做,而不是重复将数字前置到string可能会更有效,但由于数字的数量很低,您将不得不执行一个基准确定。
需要一些额外的处理来处理非正数。
public string IntToString(int a) { if (a == 0) return "0"; if (a == int.MinValue) return "-2147483648"; var isNegative = false; if (a < 0) { a = -a; isNegative = true; } var stack = new Stack<char>(); while (a != 0) { var c = a%10 + '0'; stack.Push((char) c); a /= 10; } if (isNegative) stack.Push('-'); return new string(stack.ToArray()); }
我的第一个版本使用StringBuilder
从字符数组中创buildstring,但是将string“从” StringBuilder
取出需要调用ToString
方法。 显然,这个方法并没有对string进行任何int转换,对于我来说这个问题是关于什么的。
但是为了certificate你可以在不调用ToString
情况下创build一个string,我已经转向使用一个string
构造函数,我也认为它比使用StringBuilder
更高效。
如果禁止任何forms的ToString
,则不能使用string连接,如string.Concat
的文档中string.Concat
:
该方法通过调用arg0和arg1的无参数ToString方法连接arg0和arg1; 它不添加任何分隔符。
所以执行s += '1'
会调用'1'.ToString()
。 但对我来说这并不重要。 重要的部分是如何将int转换为string。
瞄准一个较短的版本,并使用Math.DivRem
:
string IntToString(int a) { if (a == int.MinValue) return "-2147483648"; if (a < 0) return "-" + IntToString(-a); if (a == 0) return "0"; var s = ""; do { int r; a = Math.DivRem(a, 10, out r); s = new string((char)(r + (int)'0'), 1) + s; } while (a > 0); return s; }
new string(..., 1)
构造函数的使用只是满足OP要求ToString
不被任何东西调用的一种方法。
这里是我使用运行时分析迭代和recursion。
public static class IntegerToString { static char[] d = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); public static string Iteration(int num, int radix = 10) { if (num == 0) return "0"; if (num < 0) return "-" + Iteration(Math.Abs(num)); var r = new List<char>(); while (num > 0) { r.Insert(0, d[num % radix]); num /= radix; } return new string(r.ToArray()); } public static string Recursion(int num, int radix = 10) { if (num == 0) return "0"; if (num < 0) return "-" + Recursion(Math.Abs(num)); return (num > radix - 1 ? Recursion(num / radix) : "") + d[num % radix]; } }
关键点
- 处理基地2到36( CAVEAT:你必须确保你的基地是正确的,因为没有exception处理。
- recursion方法只有3行! (代码高尔夫风格)
分析
以下是两种方法的运行时分析,与我的电脑上的标准ToString()
进行比较。
50 runs of 100000 items per set Running Time: Iteration: 00:00:02.3459591 (00:00:00.0469191 avg) Recursion: 00:00:02.1359731 (00:00:00.0427194 avg) Standard : 00:00:00.4271253 (00:00:00.0085425 avg) Ratios: | Iter | Rec | Std -----+------+------+----- Iter | 1.00 | 0.91 | 0.18 Rec | 1.10 | 1.00 | 0.20 Std | 5.49 | 5.00 | 1.00
结果表明迭代和recursion方法比标准的ToString()
方法运行速度慢5.49和5.00倍。
这里是我用于分析的代码:
class Program { static void Main(string[] args) { var r = new Random(); var sw = new System.Diagnostics.Stopwatch(); var loop = new List<long>(); var recr = new List<long>(); var std = new List<long>(); var setSize = 100000; var runs = 50; Console.WriteLine("{0} runs of {1} items per set", runs, setSize); for (int j = 0; j < runs; j++) { // create number set var numbers = Enumerable.Range(1, setSize) .Select(s => r.Next(int.MinValue, int.MaxValue)) .ToArray(); // loop sw.Start(); for (int i = 0; i < setSize; i++) IntegerToString.Iteration(numbers[i]); sw.Stop(); loop.Add(sw.ElapsedTicks); // recursion sw.Reset(); sw.Start(); for (int i = 0; i < setSize; i++) IntegerToString.Recursion(numbers[i]); sw.Stop(); recr.Add(sw.ElapsedTicks); // standard sw.Reset(); sw.Start(); for (int i = 0; i < setSize; i++) numbers[i].ToString(); sw.Stop(); std.Add(sw.ElapsedTicks); } Console.WriteLine(); Console.WriteLine("Running Time:"); Console.WriteLine("Iteration: {0} ({1} avg)", TimeSpan.FromTicks(loop.Sum()), TimeSpan.FromTicks((int)loop.Average())); Console.WriteLine("Recursion: {0} ({1} avg)", TimeSpan.FromTicks(recr.Sum()), TimeSpan.FromTicks((int)recr.Average())); Console.WriteLine("Standard : {0} ({1} avg)", TimeSpan.FromTicks(std.Sum()), TimeSpan.FromTicks((int)std.Average())); double lSum = loop.Sum(); double rSum = recr.Sum(); double sSum = std.Sum(); Console.WriteLine(); Console.WriteLine("Ratios: \n" + " | Iter | Rec | Std \n" + "-----+------+------+-----"); foreach (var div in new[] { new {n = "Iter", t = lSum}, new {n = "Rec ", t = rSum}, new {n = "Std ", t = sSum}}) Console.WriteLine("{0} | {1:0.00} | {2:0.00} | {3:0.00}", div.n, lSum / div.t, rSum / div.t, sSum / div.t); Console.ReadLine(); }
你可以将任何数字转换为像这样的字符
byte = (char)(byte)(digit+48)
幻数48
是char 0
的ASCII值,并且它们在ASCII表中是连续的,因此您只需添加数字即可在ASCII表中获取相应的值。 你可以使用模数运算符迭代地获得整数中的数字借用pswg中的一般结构
public string IntToString(int a) { var str = string.Empty; bool isNegative = false; if (a < 0) { isNegative = true; a = -a; } do { str = (char)(byte)((a % 10) + 48) + str; a /= 10; } while(a > 0); return isNegative ? '-' + str : str }
public static string integerToString(int integerPassedIn) { if (integerPassedIn == 0) return "0"; var negative = integerPassedIn < 0; var res = new List<char>(); while(integerPassedIn != 0) { res.Add((char)(48 + Math.Abs(integerPassedIn % 10))); integerPassedIn /= 10; } res.Reverse(); if (negative) res.Insert(0, '-'); return new string(res.ToArray()); }
recursion:
public static string integerToString(int integerPassedIn) { ICollection<char> res = new List<char>(); IntToStringRecusion(integerPassedIn, res); if (integerPassedIn < 0) res.Add('-'); return new string(res.Reverse().ToArray()).PadLeft(1,'0'); } static void IntToStringRecusion(int integerPassedIn, ICollection<char> array) { if (integerPassedIn == 0) return; array.Add((char)(48 + Math.Abs(integerPassedIn % 10))); IntToStringRecusion(integerPassedIn / 10, array); }
如果这是c ++,那么:
public string IntToString(int a){ char rc[20]; int x = a; if(a < 0) x = -x; char *p = rc + 19; *p = 0; do *--p = (x % 10) | '0'; while(x /= 10); if(a < 0) *--p = '-'; return string(p); }