others - C#中如何在不使用 ToString()的情况下将Int转换为字符串?

将下列int参数转换为字符串而不使用任何本机toString函数。


public string integerToString(int integerPassedIn){ 
 //Your code here
}

ObjectObject继承都有一个ToString()方法,如何将int转换为不使用本机ToString()方法的string

如何在C#中将整数转换为字符串而不使用ToString()

时间:

像这样:


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


更新:这里的另一个版本更短,应该执行得更好,因为它消除了所有字符串连接,有利于操作固定长度的数组。它支持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);
}

这是我一直使用的解决方案:


 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'。

数字被推送到堆栈上,因为它们必须按照处理( 最重要的数字到最小有效数字)的相反顺序显示。 这样做,而不是反复将数字字符串可以更有效,但因为数字的数量很低你必须执行基准可以肯定的。

处理non-positive数字需要一些额外处理。


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 来创建字符串数组中的字符串,但是获取字符串"退出" StringBuilder 需要调用名为 ToString的方法。 显然,这个方法对字符串转换没有任何作用,对于我来说这个问题就是。

但是为了证明你可以创建一个不调用 ToString的字符串,我已经切换到了使用 string 构造函数的构造函数,它比使用 StringBuilder 更高效。

如果 ToString 禁止任何形式的你不能使用字符串连接的文档 string.Concat :

方法连接arg0和arg1by调用arg0和arg1的无参数ToString方法;它不添加任何分隔符。

所以执行 s +='1' 将调用 '1'.ToString() 。 但对我来说这并不重要。 重要的部分是如何将int转换为字符串。

针对较短版本,并使用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)构造函数只是一种方法,可以满足不需要调用ToString的操作的要求。

你可以将任意数字转换为如下字符


byte = (char)(byte)(digit+48)

in 48是char 0的ASCII值,它们是在ASCII表中序列化的,因此可以在ASCII表中添加数字,你可以使用模量运算符%来获得整数中的数字。


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 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进制 (警告: 你必须确保你的进制是正确的,因为没有异常处理,
  • 递归方法只有3行长!(golf代码风格)

分析

下面是两种方法的运行时分析,与我的计算机上的标准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

结果表明迭代和递归方法比标准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();
 }


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

递归:


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

...