简体   繁体   English

如何在不使用 ToString() 的情况下在 C# 中将 Int 转换为 String?

[英]How do I convert an Int to a String in C# without using ToString()?

Convert the following int argument into a string without using any native toString functionality.将以下 int 参数转换为字符串,而不使用任何本机 toString 功能。

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

Since everything inherits from Object and Object has a ToString() method how would you convert an int to a string without using the native ToString() method?由于所有东西都继承自Object并且Object有一个ToString()方法,你如何在不使用本机ToString()方法的情况下将int转换为string

The problem with string concatenation is that it will call ToString() up the chain until it hits one or hits the Object class.字符串连接的问题在于它会在链上调用ToString()直到它命中一个或命中Object类。

How do you convert an integer to a string in C# without using ToString() ?如何在不使用ToString()情况下在 C# 中将整数转换为字符串?

Something like this:像这样的东西:

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

Update: Here's another version which is shorter and should perform much better, since it eliminates all string concatenation in favor of manipulating a fixed-length array.更新:这是另一个更短且性能更好的版本,因为它消除了所有字符串连接,有利于操作固定长度的数组。 It supports bases up to 16, but it would be easy to extend it to higher bases.它最多支持 16 个基数,但很容易将其扩展到更高的基数。 It could probably be improved further:它可能可以进一步改进:

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

This is the solution I always use:这是我一直使用的解决方案:

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

This looks quite complicated but has the following features:这看起来很复杂,但具有以下特点:

  • Supports radix 2 to 36支持基数 2 到 36
  • Handles negative values处理负值
  • Optional total number of digits可选的总位数

I am not truly convinced the concatenation operator + calls ToString , but if that is indeed true, you can avoid those two by doing something like the following:我并不真正相信连接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());

The integer is processed from the least significant digit to the most significant.整数从最低有效位到最高有效位进行处理。 A single digit is computed used modulo 10 (%10) which is then added to the character value of '0'.使用模 10 (%10) 计算单个数字,然后将其添加到字符值“0”。 This results in one of the characters '0', '1', ... , '9'.这导致字符“0”、“1”、...、“9”之一。

The digits are pushed onto a stack because they have to be presented in the reverse order as they are processed (most significant digit to least significant digit).这些数字被压入堆栈,因为它们在处理时必须以相反的顺序呈现(最高有效数字到最低有效数字)。 Doing it like this instead of repeatedly prepending the digits to a string could be more efficient but because the number of digits is quite low you would have to perform a benchmark to be sure.这样做而不是重复将数字添加到字符串可能会更有效,但由于数字的数量非常少,您必须执行基准测试才能确定。

Some extra processing is required to handle non-positive numbers.需要一些额外的处理来处理非正数。

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

My first version used a StringBuilder to create the string from the array of characters but getting the string "out of the" StringBuilder requires a call to method named ToString .我的第一个版本使用StringBuilder创建从字符数组,但得到的字符串“走出”的字符串StringBuilder要求方法的调用命名ToString Obviously, this method does not do any int to string conversion which to me is what this question is about.显然,这个方法没有做任何 int 到字符串的转换,这对我来说就是这个问题的意义所在。

But to prove that you can create a string without calling ToString I have switched to using a string constructor which I also would assume to be more efficient compared to using a StringBuilder .但是为了证明你可以在不调用ToString情况下创建一个字符串,我已经切换到使用string构造函数,我也认为它比使用StringBuilder更有效。

And if ToString in any form is prohibited you cannot use string concatenation as seen in the documentation for string.Concat :如果禁止任何形式的ToString ,则不能使用string.Concat文档中string.Concat字符串连接:

The method concatenates arg0 and arg1by calling the parameterless ToString method of arg0 and arg1;该方法通过调用arg0和arg1的无参数ToString方法连接arg0和arg1; it does not add any delimiters.它不添加任何分隔符。

so executing s += '1' will call '1'.ToString() .所以执行s += '1'将调用'1'.ToString() But to me this isn't important.但对我来说这并不重要。 The important part is how you convert an int to a string.重要的部分是如何将 int 转换为字符串。

Aiming for a shorter version, and one that uses Math.DivRem :针对较短的版本,以及使用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;
}

The use of the new string(..., 1) constructor is just a way to satisfy the OP's requirement that ToString not be called on anything.使用new string(..., 1)构造函数只是满足 OP 要求的一种方法,即不得对任何东西调用ToString

you can convert any digit to a char like this您可以像这样将任何数字转换为字符

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

The magic number 48 is the ASCII value of the char 0 and they are sequential in the ASCII table thus you can just add the digit to get the corresponding value in the ASCII table.幻数48是字符0的 ASCII 值,它们在 ASCII 表中是连续的,因此您只需添加数字即可获得 ASCII 表中的相应值。 and you can get the digits in the integer iteratively using the modulus operator % Borrowing the general structure from pswg you would get并且您可以使用模数运算符以迭代方式获取整数中的数字%借用 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
}

Here are my takes on it using iteration and recursion with run time analysis.这是我使用迭代和递归以及运行时分析对其的看法。

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


Key Points要点

  • Handles base 2 up to 36 ( CAVEAT: you'll have to ensure your base is correct since there is no exception handling.处理基数 2 到 36(注意:您必须确保基数正确,因为没有异常处理。
  • Recursion method is only 3 lines long!递归方法只有3行! (code golf style) (代码高尔夫风格)

Analysis分析

The following is the runtime analysis of both methods compared to the standard ToString() on my computer.以下是这两种方法与我电脑上的标准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

The results indicate the iteration and recursion methods run 5.49 and 5.00 times slower than the standard ToString() method.结果表明迭代和递归方法的运行速度比标准ToString()方法慢 5.49 和 5.00 倍。

And here is the code I used for the analysis:这是我用于分析的代码:

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

If this is c++, then: 如果这是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);
}

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

easy peasy:简单的馅饼:

string s = 5 + ""
//s = "5"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM