在.NET 中将基数10转换为任意基数的最快方法?

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

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

它并不是那么的快速和简洁。在.NET 中有没有一种好的、众所周知的方法来实现这一点呢?

我正在寻找的东西,让我使用 任何的基础与任意字符串使用。

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

Convert.ToString(1, x);

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

有人知道在 C # 中实现这一点的一种好的、高效的方法吗?

157707 次浏览

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

例如:

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

然而,正如注释所指出的,Convert.ToString只支持以下有限的——但通常是足够的——基数集: 2、8、10或16。

更新(满足转换到任何基地的要求) :

我不知道在 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);
}
}

更新2(表现改善)

使用数组缓冲区而不是字符串串联来构建结果字符串可以提高性能,特别是在大数量情况下(参见方法 IntToStringFast)。在最好的情况下(即尽可能长的输入) ,这种方法大约快三倍。不过,对于1位数字(即目标基数中的1位数字) ,IntToString会较快。

这个论坛帖子的这门课能帮助你吗?

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;


}


}

完全未经测试... 让我知道如果它的工作! (复制粘贴它,以防论坛帖子消失或其他...)

我使用它将 Guid 存储为一个较短的字符串(但仅限于使用106个字符)。 如果有人感兴趣,这里是我的代码,用于将字符串解码回数值(在本例中,我使用2个乌龙作为 Guid 值,而不是编码一个 Int128(因为我在3.5而不是4.0中)。 为了清楚起见,CODE 是一个包含106个独特字符的字符串 const。

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

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

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

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

原子悖论

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

我也有类似的需求,除了我还需要对“数字”做数学运算。我在这里采纳了一些建议,并创建了一个类,将做所有这些有趣的东西。它允许任何 Unicode字符被用来表示一个数字,它也适用于小数。

这个类很容易使用。只需创建一个类型为 New BaseNumber的数字,设置一些属性,然后关闭。这些例程会自动在基数10和基数 x 之间切换,你设置的值保留在你设置的基数中,所以不会丢失精度(直到转换,但即使那样,精度损失应该是非常小的,因为这个例程在任何可能的地方使用 DoubleLong)。

我不能指挥这支舞的速度。它可能相当慢,所以我不确定它是否适合提出问题的人的需要,但它肯定是灵活的,所以希望其他人可以使用它。

对于可能需要此代码来计算 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

现在开始循环 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

您将注意到 Excel 部分的重要部分是,0在基于 re 的数字中由一个@标识。所以我只需要过滤掉所有包含@的数字,然后得到正确的序列(A,B,C,... ,Z,AA,AB,AC,...)。

对于这个问题,我已经很晚了,但是我最近为一个正在进行的项目编写了以下 helper 类。它的设计目的是将短字符串转换为数字,然后再转换回来(一个简单的 完美的大麻函数) ,但是它也可以在任意基数之间执行数字转换。Base10ToString方法实现回答了最初发布的问题。

需要传递给类构造函数的 shouldSupportRoundTripping标志,以防止在转换到以10为基数再转换回来的过程中丢失数字字符串的前导数字(考虑到我的要求,这是至关重要的!).大多数情况下,从数字字符串中删除前导0可能不是问题。

不管怎样,这是密码:

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 = '\0';


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

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

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

代码应该是这样的:

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

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

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

快速“ 来自”和“ ”方法

我迟到了,但是我把以前的答案复合起来,并且改进了它们。我认为这两种方法比目前发布的任何其他方法都要快。我能够在400毫秒内在一台核心机器上将100万个数字从36进制转换为36进制。

下面的例子是 基数62。更改 BaseChars数组以便从任何其他基进行转换。

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

编辑(2018-07-12)

修正了由@AdrianBotor (参见注释)发现的将46655转换为基数36的角箱问题。这是由于计算 Math.Log(46656, 36)的一个小的浮点错误引起的,正好是3,但是。NET 返回 3 + 4.44e-16,这会导致输出缓冲区中出现额外的字符。

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




}
}
}

这是基于帕维尔的答案,但是消除了对负数不必要的字符串连接。而且基数是由传入的字符定义的,所以如果你想用字符 ABC 把一个数字转换成基数3,传入“ ABC”——字符串长度3就是基数:

C #

public static string ToBase(long base10, string baseChars = "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZ")
{
if (baseChars.Length < 2)
throw new ArgumentException("baseChars must be at least 2 chars long");
if (base10 == 0L)
return baseChars[0].ToString();
bool isNegative = base10 < 0L;
int radix = baseChars.Length;
int index = 64; // because it's how long a string will be if the basechars are 2 long (binary)
var chars = new char[index + 1]; // 65 chars, 64 from above plus one for sign if it's negative
base10 = Math.Abs(base10);
while (base10 > 0L)
{
chars[index] = baseChars[(int)(base10 % radix)];
base10 /= radix;
index--;
}


if (isNegative)
{
chars[index] = '-';
index--;
}


return new string(chars, index + 1, chars.Length - index - 1);
}

VB.NET

Public Shared Function ToBase(base10 As Long, Optional baseChars As String = "0123456789ABCDEFGHIJKLMNOPQRTSUVWXYZ") As String


If baseChars.Length < 2 Then Throw New ArgumentException("baseChars must be at least 2 chars long")


If base10 = 0 Then Return baseChars(0)


Dim isNegative = base10 < 0
Dim radix = baseChars.Length
Dim index As Integer = 64 'because it's how long a string will be if the basechars are 2 long (binary)
Dim chars(index) As Char '65 chars, 64 from above plus one for sign if it's negative


base10 = Math.Abs(base10)




While base10 > 0
chars(index) = baseChars(base10 Mod radix)
base10 \= radix


index -= 1
End While


If isNegative Then
chars(index) = "-"c
index -= 1
End If


Return New String(chars, index + 1, UBound(chars) - index)


End Function

这是一种相当简单的方法,但可能不是最快的。它非常强大,因为它是可组合的。

public static IEnumerable<int> ToBase(this int x, int b)
{
IEnumerable<int> ToBaseReverse()
{
if (x == 0)
{
yield return 0;
yield break;
}
int z = x;
while (z > 0)
{
yield return z % b;
z = z / b;
}
}


return ToBaseReverse().Reverse();
}

结合这个简单的扩展方法,现在可以得到任何基:

public static string ToBase(this int number, string digits) =>
String.Concat(number.ToBase(digits.Length).Select(x => digits[x]));

它可以这样使用:

var result = 23.ToBase("01");
var result2 = 23.ToBase("012X");


Console.WriteLine(result);
Console.WriteLine(result2);

输出结果是:

10111
11X