C# 将基数 10 数字转换为 .NET 中任何基数的最快方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/923771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 03:18:45  来源:igfitidea点击:

Quickest way to convert a base 10 number to any base in .NET?

c#.netintbasenumber-systems

提问by joshcomley

I have and old(ish) C# method I wrote that takes a number and converts it to any base:

我有我写的旧(ish)C# 方法,它接受一个数字并将其转换为任何基数:

string ConvertToBase(int number, char[] baseChars);

It's not all that super speedy and neat. Is there a good, known way of achieving this in .NET?

这并不是那么快速和整洁。在 .NET 中是否有一种好的、已知的方法来实现这一点?

I'm looking for something that allows me to use anybase with an arbitrary string of characters to use.

我正在寻找允许我使用任意字符串的任何基数的东西。

This only allows bases 16, 10, 8 and 2:

这只允许基数为 16、10、8 和 2:

Convert.ToString(1, x);

I want to use this to achieve a massively high base taking advantage of numbers, all lower case and all upper case letters. Like in this thread, but for C# not JavaScript.

我想用它来利用数字、所有小写字母和所有大写字母来实现非常高的基数。就像在这个线程中一样,但对于 C# 不是 JavaScript。

Does anyone know of a good and efficient way of doing this in C#?

有没有人知道在 C# 中这样做的好方法?

采纳答案by Dirk Vollmar

Convert.ToStringcan be used to convert a number to its equivalent string representation in a specified base.

Convert.ToString可用于将数字转换为其指定基数中的等效字符串表示形式。

Example:

例子:

string binary = Convert.ToString(5, 2); // convert 5 to its binary representation
Console.WriteLine(binary);              // prints 101

However, as pointed out by the comments, Convert.ToStringonly supports the following limited - but typically sufficient - set of bases: 2, 8, 10, or 16.

但是,正如评论所指出的,Convert.ToString仅支持以下有限但通常足够的基数集:2、8、10 或 16。

Update (to meet the requirement to convert to any base):

更新(以满足转换为任何基数的要求):

I'm not aware of any method in the BCL which is capable to convert numbers to any base so you would have to write your own small utility function. A simple sample would look like that (note that this surely can be made faster by replacing the string concatenation):

我不知道 BCL 中有任何方法可以将数字转换为任何基数,因此您必须编写自己的小型实用程序函数。一个简单的示例看起来像这样(请注意,通过替换字符串连接肯定可以加快速度):

class Program
{
    static void Main(string[] args)
    {
        // convert to binary
        string binary = IntToString(42, new char[] { '0', '1' });

        // convert to hexadecimal
        string hex = IntToString(42, 
            new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                         'A', 'B', 'C', 'D', 'E', 'F'});

        // convert to hexavigesimal (base 26, A-Z)
        string hexavigesimal = IntToString(42, 
            Enumerable.Range('A', 26).Select(x => (char)x).ToArray());

        // convert to sexagesimal
        string xx = IntToString(42, 
            new char[] { '0','1','2','3','4','5','6','7','8','9',
            'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
            'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x'});
    }

    public static string IntToString(int value, char[] baseChars)
    {
        string result = string.Empty;
        int targetBase = baseChars.Length;

        do
        {
            result = baseChars[value % targetBase] + result;
            value = value / targetBase;
        } 
        while (value > 0);

        return result;
    }

    /// <summary>
    /// An optimized method using an array as buffer instead of 
    /// string concatenation. This is faster for return values having 
    /// a length > 1.
    /// </summary>
    public static string IntToStringFast(int value, char[] baseChars)
    {
        // 32 is the worst cast buffer size for base 2 and int.MaxValue
        int i = 32;
        char[] buffer = new char[i];
        int targetBase= baseChars.Length;

        do
        {
            buffer[--i] = baseChars[value % targetBase];
            value = value / targetBase;
        }
        while (value > 0);

        char[] result = new char[32 - i];
        Array.Copy(buffer, i, result, 0, 32 - i);

        return new string(result);
    }
}

Update 2 (Performance Improvement)

更新 2(性能改进)

Using an array buffer instead of string concatenation to build the result string gives a performance improvement especially on large number (see method IntToStringFast). In the best case (i.e. the longest possible input) this method is roughly three times faster. However, for 1-digit numbers (i.e. 1-digit in the target base), IntToStringwill be faster.

使用数组缓冲区而不是字符串连接来构建结果字符串可以提高性能,尤其是在大数字上(参见方法IntToStringFast)。在最好的情况下(即最长的可能输入),这种方法大约快三倍。但是,对于 1 位数字(即目标基数中的 1 位数字),IntToString速度会更快。

回答by Svish

Could this class from this forum posthelp you?

这个论坛帖子中的这门课可以帮助你吗?

public class BaseConverter { 

public static string ToBase(string number, int start_base, int target_base) { 

  int base10 = this.ToBase10(number, start_base); 
  string rtn = this.FromBase10(base10, target_base); 
  return rtn; 

} 

public static int ToBase10(string number, int start_base) { 

  if (start_base < 2 || start_base > 36) return 0; 
  if (start_base == 10) return Convert.ToInt32(number); 

  char[] chrs = number.ToCharArray(); 
  int m = chrs.Length - 1; 
  int n = start_base; 
  int x; 
  int rtn = 0; 

  foreach(char c in chrs) { 

    if (char.IsNumber(c)) 
      x = int.Parse(c.ToString()); 
    else 
      x = Convert.ToInt32(c) - 55; 

    rtn += x * (Convert.ToInt32(Math.Pow(n, m))); 

    m--; 

  } 

  return rtn; 

} 

public static string FromBase10(int number, int target_base) { 

  if (target_base < 2 || target_base > 36) return ""; 
  if (target_base == 10) return number.ToString(); 

  int n = target_base; 
  int q = number; 
  int r; 
  string rtn = ""; 

  while (q >= n) { 

    r = q % n; 
    q = q / n; 

    if (r < 10) 
      rtn = r.ToString() + rtn; 
    else 
      rtn = Convert.ToChar(r + 55).ToString() + rtn; 

  } 

  if (q < 10) 
    rtn = q.ToString() + rtn; 
  else 
    rtn = Convert.ToChar(q + 55).ToString() + rtn; 

  return rtn; 

} 

}

Totally untested... let me know if it works! (Copy-pasted it in case the forum post goes away or something...)

完全未经测试......让我知道它是否有效!(复制粘贴以防论坛帖子消失或其他...)

回答by Gary

I was using this to store a Guid as a shorter string (but was limited to use 106 characters). If anyone is interested here is my code for decoding the string back to numeric value (in this case I used 2 ulongs for the Guid value, rather than coding an Int128 (since I'm in 3.5 not 4.0). For clarity CODE is a string const with 106 unique chars. ConvertLongsToBytes is pretty unexciting.

我使用它来将 Guid 存储为较短的字符串(但仅限于使用 106 个字符)。如果有人感兴趣,这里是我将字符串解码回数值的代码(在这种情况下,我使用 2 ulong 作为 Guid 值,而不是编码 Int128(因为我在 3.5 而不是 4.0)。为了清楚起见,CODE 是具有 106 个唯一字符的字符串常量。ConvertLongsToBytes 非常令人兴奋。

private static Guid B106ToGuid(string pStr)
    {
        try
        {
            ulong tMutl = 1, tL1 = 0, tL2 = 0, targetBase = (ulong)CODE.Length;
            for (int i = 0; i < pStr.Length / 2; i++)
            {
                tL1 += (ulong)CODE.IndexOf(pStr[i]) * tMutl;
                tL2 += (ulong)CODE.IndexOf(pStr[pStr.Length / 2 + i]) * tMutl;
                tMutl *= targetBase;
            }
            return new Guid(ConvertLongsToBytes(tL1, tL2));
        }
        catch (Exception ex)
        {
            throw new Exception("B106ToGuid failed to convert string to Guid", ex);
        }
    }

回答by user1031307

I too was looking for a fast way to convert decimal number to another base in the range of [2..36] so I developed the following code. Its simple to follow and uses a Stringbuilder object as a proxy for a character buffer that we can index character by character. The code appears to be very fast compared to alternatives and a lot faster than initialising individual characters in a character array.

我也在寻找一种将十进制数转换为 [2..36] 范围内的另一个基数的快速方法,因此我开发了以下代码。它易于遵循并使用 Stringbuilder 对象作为字符缓冲区的代理,我们可以逐个字符地索引它。与替代方案相比,该代码似乎非常快,并且比初始化字符数组中的单个字符要快得多。

For your own use you might prefer to: 1/ Return a blank string rather than throw an exception. 2/ remove the radix check to make the method run even faster 3/ Initialise the Stringbuilder object with 32 '0's and remove the the line result.Remove( 0, i );. This will cause the string to be returned with leading zeros and further increase the speed. 4/ Make the Stringbuilder object a static field within the class so no matter how many times the DecimalToBase method is called the Stringbuilder object is only initialised the once. If you do this change 3 above would no longer work.

对于您自己的使用,您可能更喜欢: 1/ 返回一个空白字符串而不是抛出异常。2/ 删除基数检查以使方法运行得更快 3/ 用 32 个 0 初始化 Stringbuilder 对象并删除行 result.Remove( 0, i );。这将导致字符串返回前导零并进一步提高速度。4/ 使 Stringbuilder 对象成为类中的静态字段,因此无论调用 DecimalToBase 方法多少次,Stringbuilder 对象都只初始化一次。如果您这样做,上面的更改 3 将不再有效。

I hope someone finds this useful :)

我希望有人觉得这很有用:)

AtomicParadox

原子悖论

        static string DecimalToBase(int number, int radix)
    {
        // Check that the radix is between 2 and 36 inclusive
        if ( radix < 2 || radix > 36 )
            throw new ArgumentException("ConvertToBase(int number, int radix) - Radix must be between 2 and 36.");

        // Create a buffer large enough to hold the largest int value represented in binary digits 
        StringBuilder result = new StringBuilder("                                ");  // 32 spaces

        // The base conversion calculates the digits in reverse order so use
        // an index to point to the last unused space in our buffer
        int i = 32; 

        // Convert the number to the new base
        do
        {
            int remainder = number % radix;
            number = number / radix;
            if(remainder <= 9)
                result[--i] = (char)(remainder + '0');  // Converts [0..9] to ASCII ['0'..'9']
            else
                result[--i] = (char)(remainder + '7');  // Converts [10..36] to ASCII ['A'..'Z']
        } while ( number > 0 );

        // Remove the unwanted padding from the front of our buffer and return the result
        // Note i points to the last unused character in our buffer
        result.Remove( 0, i );
        return (result.ToString());
    }

回答by cjbarth

I had a similar need, except I needed to do math on the "numbers" as well. I took some of the suggestions here and created a class that will do all this fun stuff. It allows for any unicode character to be used to represent a number and it works with decimals too.

我有类似的需求,除了我还需要对“数字”进行数学运算。我在这里接受了一些建议,并创建了一个类来完成所有这些有趣的事情。它允许使用任何 unicode 字符来表示一个数字,它也适用于小数。

This class is pretty easy to use. Just create a number as a type of New BaseNumber, set a few properties, and your off. The routines take care of switching between base 10 and base x automatically and the value you set is preserved in the base you set it in, so no accuracy is lost (until conversion that is, but even then precision loss should be very minimal since this routine uses Doubleand Longwhere ever possible).

这个类非常容易使用。只需创建一个数字作为 的类型New BaseNumber,设置一些属性,然后关闭。这些例程会自动在基数 10 和基数 x 之间切换,并且您设置的值保留在您设置的基数中,因此不会丢失准确性(直到转换,但即使如此,精度损失也应该非常小,因为这日常使用DoubleLong尽可能)。

I can't command on the speed of this routine. It is probably quite slow, so I'm not sure if it will suit the needs of the one who asked the question, but it certain is flexible, so hopefully someone else can use this.

我无法控制这个例程的速度。它可能很慢,所以我不确定它是否适合提出问题的人的需求,但它肯定是灵活的,所以希望其他人可以使用它。

For anyone else that may need this code for calculating the next column in Excel, I will include the looping code I used that leverages this class.

对于可能需要此代码来计算 Excel 中的下一列的其他任何人,我将包括我使用的利用此类的循环代码。

Public Class BaseNumber

    Private _CharacterArray As List(Of Char)

    Private _BaseXNumber As String
    Private _Base10Number As Double?

    Private NumberBaseLow As Integer
    Private NumberBaseHigh As Integer

    Private DecimalSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator
    Private GroupSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator

    Public Sub UseCapsLetters()
        'http://unicodelookup.com
        TrySetBaseSet(65, 90)
    End Sub

    Public Function GetCharacterArray() As List(Of Char)
        Return _CharacterArray
    End Function

    Public Sub SetCharacterArray(CharacterArray As String)
        _CharacterArray = New List(Of Char)
        _CharacterArray.AddRange(CharacterArray.ToList)

        TrySetBaseSet(_CharacterArray)
    End Sub

    Public Sub SetCharacterArray(CharacterArray As List(Of Char))
        _CharacterArray = CharacterArray
        TrySetBaseSet(_CharacterArray)
    End Sub

    Public Sub SetNumber(Value As String)
        _BaseXNumber = Value
        _Base10Number = Nothing
    End Sub

    Public Sub SetNumber(Value As Double)
        _Base10Number = Value
        _BaseXNumber = Nothing
    End Sub

    Public Function GetBaseXNumber() As String
        If _BaseXNumber IsNot Nothing Then
            Return _BaseXNumber
        Else
            Return ToBaseString()
        End If
    End Function

    Public Function GetBase10Number() As Double
        If _Base10Number IsNot Nothing Then
            Return _Base10Number
        Else
            Return ToBase10()
        End If
    End Function

    Private Sub TrySetBaseSet(Values As List(Of Char))
        For Each value As Char In _BaseXNumber
            If Not Values.Contains(value) Then
                Throw New ArgumentOutOfRangeException("The string has a value, " & value & ", not contained in the selected 'base' set.")
                _CharacterArray.Clear()
                DetermineNumberBase()
            End If
        Next

        _CharacterArray = Values

    End Sub

    Private Sub TrySetBaseSet(LowValue As Integer, HighValue As Integer)

        Dim HighLow As KeyValuePair(Of Integer, Integer) = GetHighLow()

        If HighLow.Key < LowValue OrElse HighLow.Value > HighValue Then
            Throw New ArgumentOutOfRangeException("The string has a value not contained in the selected 'base' set.")
            _CharacterArray.Clear()
            DetermineNumberBase()
        End If

        NumberBaseLow = LowValue
        NumberBaseHigh = HighValue

    End Sub

    Private Function GetHighLow(Optional Values As List(Of Char) = Nothing) As KeyValuePair(Of Integer, Integer)
        If Values Is Nothing Then
            Values = _BaseXNumber.ToList
        End If

        Dim lowestValue As Integer = Convert.ToInt32(Values(0))
        Dim highestValue As Integer = Convert.ToInt32(Values(0))

        Dim currentValue As Integer

        For Each value As Char In Values

            If value <> DecimalSeparator AndAlso value <> GroupSeparator Then
                currentValue = Convert.ToInt32(value)
                If currentValue > highestValue Then
                    highestValue = currentValue
                End If
                If currentValue < lowestValue Then
                    currentValue = lowestValue
                End If
            End If
        Next

        Return New KeyValuePair(Of Integer, Integer)(lowestValue, highestValue)

    End Function

    Public Sub New(BaseXNumber As String)
        _BaseXNumber = BaseXNumber
        DetermineNumberBase()
    End Sub

    Public Sub New(BaseXNumber As String, NumberBase As Integer)
        Me.New(BaseXNumber, Convert.ToInt32("0"c), NumberBase)
    End Sub

    Public Sub New(BaseXNumber As String, NumberBaseLow As Integer, NumberBaseHigh As Integer)
        _BaseXNumber = BaseXNumber
        Me.NumberBaseLow = NumberBaseLow
        Me.NumberBaseHigh = NumberBaseHigh
    End Sub

    Public Sub New(Base10Number As Double)
        _Base10Number = Base10Number
    End Sub

    Private Sub DetermineNumberBase()
        Dim highestValue As Integer

        Dim currentValue As Integer

        For Each value As Char In _BaseXNumber

            currentValue = Convert.ToInt32(value)
            If currentValue > highestValue Then
                highestValue = currentValue
            End If
        Next

        NumberBaseHigh = highestValue
        NumberBaseLow = Convert.ToInt32("0"c) 'assume 0 is the lowest

    End Sub

    Private Function ToBaseString() As String
        Dim Base10Number As Double = _Base10Number

        Dim intPart As Long = Math.Truncate(Base10Number)
        Dim fracPart As Long = (Base10Number - intPart).ToString.Replace(DecimalSeparator, "")

        Dim intPartString As String = ConvertIntToString(intPart)
        Dim fracPartString As String = If(fracPart <> 0, DecimalSeparator & ConvertIntToString(fracPart), "")

        Return intPartString & fracPartString

    End Function

    Private Function ToBase10() As Double
        Dim intPartString As String = _BaseXNumber.Split(DecimalSeparator)(0).Replace(GroupSeparator, "")
        Dim fracPartString As String = If(_BaseXNumber.Contains(DecimalSeparator), _BaseXNumber.Split(DecimalSeparator)(1), "")

        Dim intPart As Long = ConvertStringToInt(intPartString)
        Dim fracPartNumerator As Long = ConvertStringToInt(fracPartString)
        Dim fracPartDenominator As Long = ConvertStringToInt(GetEncodedChar(1) & String.Join("", Enumerable.Repeat(GetEncodedChar(0), fracPartString.ToString.Length)))

        Return Convert.ToDouble(intPart + fracPartNumerator / fracPartDenominator)

    End Function

    Private Function ConvertIntToString(ValueToConvert As Long) As String
        Dim result As String = String.Empty
        Dim targetBase As Long = GetEncodingCharsLength()

        Do
            result = GetEncodedChar(ValueToConvert Mod targetBase) & result
            ValueToConvert = ValueToConvert \ targetBase
        Loop While ValueToConvert > 0

        Return result
    End Function

    Private Function ConvertStringToInt(ValueToConvert As String) As Long
        Dim result As Long
        Dim targetBase As Integer = GetEncodingCharsLength()
        Dim startBase As Integer = GetEncodingCharsStartBase()

        Dim value As Char
        For x As Integer = 0 To ValueToConvert.Length - 1
            value = ValueToConvert(x)
            result += GetDecodedChar(value) * Convert.ToInt32(Math.Pow(GetEncodingCharsLength, ValueToConvert.Length - (x + 1)))
        Next

        Return result

    End Function

    Private Function GetEncodedChar(index As Integer) As Char
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray(index)
        Else
            Return Convert.ToChar(index + NumberBaseLow)
        End If
    End Function

    Private Function GetDecodedChar(character As Char) As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray.IndexOf(character)
        Else
            Return Convert.ToInt32(character) - NumberBaseLow
        End If
    End Function

    Private Function GetEncodingCharsLength() As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray.Count
        Else
            Return NumberBaseHigh - NumberBaseLow + 1
        End If
    End Function

    Private Function GetEncodingCharsStartBase() As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return GetHighLow.Key
        Else
            Return NumberBaseLow
        End If
    End Function
End Class

And now for the code to loop through Excel columns:

现在让代码循环遍历 Excel 列:

    Public Function GetColumnList(DataSheetID As String) As List(Of String)
        Dim workingColumn As New BaseNumber("A")
        workingColumn.SetCharacterArray("@ABCDEFGHIJKLMNOPQRSTUVWXYZ")

        Dim listOfPopulatedColumns As New List(Of String)
        Dim countOfEmptyColumns As Integer

        Dim colHasData As Boolean
        Dim cellHasData As Boolean

        Do
            colHasData = True
            cellHasData = False
            For r As Integer = 1 To GetMaxRow(DataSheetID)
                cellHasData = cellHasData Or XLGetCellValue(DataSheetID, workingColumn.GetBaseXNumber & r) <> ""
            Next
            colHasData = colHasData And cellHasData

            'keep trying until we get 4 empty columns in a row
            If colHasData Then
                listOfPopulatedColumns.Add(workingColumn.GetBaseXNumber)
                countOfEmptyColumns = 0
            Else
                countOfEmptyColumns += 1
            End If

            'we are already starting with column A, so increment after we check column A
            Do
                workingColumn.SetNumber(workingColumn.GetBase10Number + 1)
            Loop Until Not workingColumn.GetBaseXNumber.Contains("@")

        Loop Until countOfEmptyColumns > 3

        Return listOfPopulatedColumns

    End Function

You'll note the important part of the Excel part is that 0 is identified by a @ in the re-based number. So I just filter out all the numbers that have an @ in them and I get the proper sequence (A, B, C, ..., Z, AA, AB, AC, ...).

您会注意到 Excel 部分的重要部分是 0 由重新基数中的 @ 标识。所以我只是过滤掉所有带有 @ 的数字,然后我得到正确的序列(A、B、C、...、Z、AA、AB、AC、...)。

回答by Pavel Vladov

I recently blogged about this. My implementation does not use any string operations during the calculations, which makes it very fast. Conversion to any numeral system with base from 2 to 36 is supported:

我最近写了一篇关于这个的博客。我的实现在计算过程中不使用任何字符串操作,这使得它非常快。支持转换为任何基数为 2 到 36 的数字系统:

/// <summary>
/// Converts the given decimal number to the numeral system with the
/// specified radix (in the range [2, 36]).
/// </summary>
/// <param name="decimalNumber">The number to convert.</param>
/// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
/// <returns></returns>
public static string DecimalToArbitrarySystem(long decimalNumber, int radix)
{
    const int BitsInLong = 64;
    const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    if (radix < 2 || radix > Digits.Length)
        throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());

    if (decimalNumber == 0)
        return "0";

    int index = BitsInLong - 1;
    long currentNumber = Math.Abs(decimalNumber);
    char[] charArray = new char[BitsInLong];

    while (currentNumber != 0)
    {
        int remainder = (int)(currentNumber % radix);
        charArray[index--] = Digits[remainder];
        currentNumber = currentNumber / radix;
    }

    string result = new String(charArray, index + 1, BitsInLong - index - 1);
    if (decimalNumber < 0)
    {
        result = "-" + result;
    }

    return result;
}

I've also implemented a fast inverse function in case anyone needs it too: Arbitrary to Decimal Numeral System.

我还实现了一个快速逆函数,以防有人也需要它: 任意到十进制数字系统

回答by Steve Rands

Very late to the party on this one, but I wrote the following helper class recently for a project at work. It was designed to convert short strings into numbers and back again (a simplistic perfect hashfunction), however it will also perform number conversion between arbitrary bases. The Base10ToStringmethod implementation answers the question that was originally posted.

参加这个聚会很晚,但我最近为工作中的一个项目编写了以下帮助类。它旨在将短字符串转换为数字然后再转换回来(一个简单的完美哈希函数),但它也将执行任意基数之间的数字转换。该Base10ToString方法实施回答了最初发布的问题。

The shouldSupportRoundTrippingflag passed to the class constructor is needed to prevent the loss of leading digits from the number string during conversion to base-10 and back again (crucial, given my requirements!). Most of the time the loss of leading 0s from the number string probably won't be an issue.

shouldSupportRoundTripping需要传递给类构造函数的标志来防止在转换为 base-10 并再次转换期间丢失数字字符串中的前导数字(根据我的要求,这很重要!)。大多数情况下,从数字字符串中丢失前导 0 可能不是问题。

Anyway, here's the code:

无论如何,这是代码:

using System;
using System.Collections.Generic;
using System.Linq;

namespace StackOverflow
{
    /// <summary>
    /// Contains methods used to convert numbers between base-10 and another numbering system.
    /// </summary>
    /// <remarks>
    /// <para>
    /// This conversion class makes use of a set of characters that represent the digits used by the target
    /// numbering system. For example, binary would use the digits 0 and 1, whereas hex would use the digits
    /// 0 through 9 plus A through F. The digits do not have to be numerals.
    /// </para>
    /// <para>
    /// The first digit in the sequence has special significance. If the number passed to the
    /// <see cref="StringToBase10"/> method has leading digits that match the first digit, then those leading
    /// digits will effectively be 'lost' during conversion. Much of the time this won't matter. For example,
    /// "0F" hex will be converted to 15 decimal, but when converted back to hex it will become simply "F",
    /// losing the leading "0". However, if the set of digits was A through Z, and the number "ABC" was
    /// converted to base-10 and back again, then the leading "A" would be lost. The <see cref="System.Boolean"/>
    /// flag passed to the constructor allows 'round-tripping' behaviour to be supported, which will prevent
    /// leading digits from being lost during conversion.
    /// </para>
    /// <para>
    /// Note that numeric overflow is probable when using longer strings and larger digit sets.
    /// </para>
    /// </remarks>
    public class Base10Converter
    {
        const char NullDigit = '
namespace StackOverflow
{
    public sealed class BinaryNumberConverter : Base10Converter
    {
        public BinaryNumberConverter()
            : base(digits: "01", shouldSupportRoundTripping: false)
        {
        }
    }

    public sealed class HexNumberConverter : Base10Converter
    {
        public HexNumberConverter()
            : base(digits: "0123456789ABCDEF", shouldSupportRoundTripping: false)
        {
        }
    }
}
'; public Base10Converter(string digits, bool shouldSupportRoundTripping = false) : this(digits.ToCharArray(), shouldSupportRoundTripping) { } public Base10Converter(IEnumerable<char> digits, bool shouldSupportRoundTripping = false) { if (digits == null) { throw new ArgumentNullException("digits"); } if (digits.Count() == 0) { throw new ArgumentException( message: "The sequence is empty.", paramName: "digits" ); } if (!digits.Distinct().SequenceEqual(digits)) { throw new ArgumentException( message: "There are duplicate characters in the sequence.", paramName: "digits" ); } if (shouldSupportRoundTripping) { digits = (new[] { NullDigit }).Concat(digits); } _digitToIndexMap = digits .Select((digit, index) => new { digit, index }) .ToDictionary(keySelector: x => x.digit, elementSelector: x => x.index); _radix = _digitToIndexMap.Count; _indexToDigitMap = _digitToIndexMap .ToDictionary(keySelector: x => x.Value, elementSelector: x => x.Key); } readonly Dictionary<char, int> _digitToIndexMap; readonly Dictionary<int, char> _indexToDigitMap; readonly int _radix; public long StringToBase10(string number) { Func<char, int, long> selector = (c, i) => { int power = number.Length - i - 1; int digitIndex; if (!_digitToIndexMap.TryGetValue(c, out digitIndex)) { throw new ArgumentException( message: String.Format("Number contains an invalid digit '{0}' at position {1}.", c, i), paramName: "number" ); } return Convert.ToInt64(digitIndex * Math.Pow(_radix, power)); }; return number.Select(selector).Sum(); } public string Base10ToString(long number) { if (number < 0) { throw new ArgumentOutOfRangeException( message: "Value cannot be negative.", paramName: "number" ); } string text = string.Empty; long remainder; do { number = Math.DivRem(number, _radix, out remainder); char digit; if (!_indexToDigitMap.TryGetValue((int) remainder, out digit) || digit == NullDigit) { throw new ArgumentException( message: "Value cannot be converted given the set of digits used by this converter.", paramName: "number" ); } text = digit + text; } while (number > 0); return text; } } }

This can also be subclassed to derive custom number converters:

这也可以被子类化以派生自定义数字转换器:

using System.Diagnostics;

namespace StackOverflow
{
    class Program
    {
        static void Main(string[] args)
        {
            {
                var converter = new Base10Converter(
                    digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz",
                    shouldSupportRoundTripping: true
                    );

                long number = converter.StringToBase10("Atoz");
                string text = converter.Base10ToString(number);
                Debug.Assert(text == "Atoz");
            }

            {
                var converter = new HexNumberConverter();

                string text = converter.Base10ToString(255);
                long number = converter.StringToBase10(text);
                Debug.Assert(number == 255);
            }
        }
    }
}

And the code would be used like this:

代码将像这样使用:

public static string Int32ToString(int value, int toBase)
{
    string result = string.Empty;
    do
    {
        result = "0123456789ABCDEF"[value % toBase] + result;
        value /= toBase;
    }
    while (value > 0);

    return result;
}

回答by Kresimir

One can also use slightly modified version of the accepted one and adjust base characters string to it's needs:

还可以使用已接受版本的稍微修改版本,并根据需要调整基本字符串:

private static readonly char[] BaseChars = 
         "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
private static readonly Dictionary<char, int> CharValues = BaseChars
           .Select((c,i)=>new {Char=c, Index=i})
           .ToDictionary(c=>c.Char,c=>c.Index);

public static string LongToBase(long value)
{
   long targetBase = BaseChars.Length;
   // Determine exact number of characters to use.
   char[] buffer = new char[Math.Max( 
              (int) Math.Ceiling(Math.Log(value + 1, targetBase)), 1)];

   var i = buffer.Length;
   do
   {
       buffer[--i] = BaseChars[value % targetBase];
       value = value / targetBase;
   }
   while (value > 0);

   return new string(buffer, i, buffer.Length - i);
}

public static long BaseToLong(string number) 
{ 
    char[] chrs = number.ToCharArray(); 
    int m = chrs.Length - 1; 
    int n = BaseChars.Length, x;
    long result = 0; 
    for (int i = 0; i < chrs.Length; i++)
    {
        x = CharValues[ chrs[i] ];
        result += x * (long)Math.Pow(n, m--);
    }
    return result;  
} 

回答by Diego

FAST "FROM" AND "TO" METHODS

快速“”和“”方法

I am late to the party, but I compounded previous answers and improved over them. I think these two methods are faster than any others posted so far. I was able to convert 1,000,000 numbers from and to base 36 in under 400ms in a single core machine.

我参加聚会迟到了,但我综合了以前的答案并改进了它们。我认为这两种方法比迄今为止发布的任何其他方法都快。我能够在 400 毫秒内在单核机器中将 1,000,000 个数字从 36 转换为基数。

Example below is for base 62. Change the BaseCharsarray to convert from and to any other base.

下面的示例适用于基数 62。更改BaseChars数组以在任何其他基数之间进行转换。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConvertToAnyBase
{
   class Program
    {
        static void Main(string[] args)
        {
            var baseNumber = int.Parse(Console.ReadLine());
            var number = int.Parse(Console.ReadLine());
            string conversion = "";


            while(number!=0)
            {

                conversion += Convert.ToString(number % baseNumber);
                number = number / baseNumber;
            }
            var conversion2 = conversion.ToArray().Reverse();
            Console.WriteLine(string.Join("", conversion2));


       }
    }
}

EDIT (2018-07-12)

编辑 (2018-07-12)

Fixed to address the corner case found by @AdrianBotor (see comments) converting 46655 to base 36. This is caused by a small floating-point error calculating Math.Log(46656, 36)which is exactly 3, but .NET returns 3 + 4.44e-16, which causes an extra character in the output buffer.

修复了@AdrianBotor 发现的极端情况(见评论)将 46655 转换为基数 36。这是由于计算Math.Log(46656, 36)恰好为 3的小浮点错误引起的,但 .NET 返回3 + 4.44e-16,这会导致输出缓冲区中出现一个额外的字符.

回答by Martin Dimitrov

##代码##