最快的方式来转换基地的10号码在.NET中的任何基地?
我有和旧的(ISH)C#方法我写了一个数字并将其转换为任何基地:
string ConvertToBase(int number, char[] baseChars);
这并不是所有超级快速和整齐。 有没有一个很好的,已知的方式在.NET中实现这一点?
我正在寻找的东西,使我可以使用任何字符的任意string使用。
这只允许基地16,10,8和2:
Convert.ToString(1, x);
我想利用这个来实现一个庞大的基数,利用数字,所有的小写字母和所有的大写字母。 就像在这个线程中 ,但是对于C#没有JavaScript。
有谁知道在C#中这样做的一个好的和有效的方法吗?
Convert.ToString
可用于将数字转换为指定基础中的等效string表示forms。
例:
string binary = Convert.ToString(5, 2); // convert 5 to its binary representation Console.WriteLine(binary); // prints 101
但是,正如注释所指出的那样, Convert.ToString
仅支持以下有限但通常是足够的集合:2,8,10或16。
更新(以满足转换到任何基地的要求):
我不知道在BCL中有任何方法能够将数字转换为任何基础,所以您将不得不编写自己的小型实用程序function。 一个简单的例子看起来像这样(注意,通过replacestring连接可以使速度更快):
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, AZ) 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(性能改进)
使用数组缓冲区而不是string连接来构build结果string可以提高性能,特别是在大数量情况下(请参阅IntToStringFast
方法)。 在最好的情况下(即最长的input),这种方法大约快三倍。 但是,对于1位数字(即目标基数中的1位数字), IntToString
会更快。
我最近在这里发表了博客 。 我的实现在计算过程中不使用任何string操作,这使得它非常快速 。 支持从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; }
我还实现了一个快速反函数,以防万一任何人需要它: 任意十进制数字系统 。
我们也可以使用接受版本的稍微修改版本,并根据需要调整基本string:
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; }
这个晚会很晚,但是我最近为一个工作项目写了下面的帮手类。 它被devise成将短的string转换成数字,然后再回来(一个简单的完美的散列函数),但它也将执行任意基地之间的数字转换。 Base10ToString
方法实现回答最初发布的问题。
传递给类构造函数的shouldSupportRoundTripping
标志是为了防止在转换到base-10的过程中丢失数字string中的前导数字并返回(关键的,根据我的要求!)。 大多数情况下,从string中丢失前导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); } } } }
快速“ 从 ” 到 “ 到 ”方法
我迟到了,但是我复制了以前的答案,改进了他们。 我认为这两种方法比迄今为止发布的其他方法都快。
下面的例子是基地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 = (long) buffer.Length; do { buffer[--i] = BaseChars[value % targetBase]; value = value / targetBase; } while (value > 0); return new string(buffer); } 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; }
可以从这个论坛post这个课程帮助你吗?
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; } }
完全未经testing…让我知道,如果它的工作! (复制粘贴它的情况下,论坛post消失或什么…)
我也正在寻找一种快速的方法来将十进制数转换为[2..36]的范围内的另一个基地,所以我开发了以下代码。 它很容易遵循,并使用一个Stringbuilder对象作为字符缓冲区的代理,我们可以逐个字符地进行索引。 与替代方法相比,代码看起来非常快,比初始化字符数组中的单个字符快得多。
为了您自己的使用,您可能更喜欢:1 /返回一个空白的string,而不是抛出一个exception。 2 /删除基数检查,使方法运行更快3 /初始化Stringbuilder对象与32'0和删除行结果。删除(0,我);。 这将导致string返回前导零,并进一步提高速度。 4 /使Stringbuilder对象成为类中的静态字段,所以不pipe调用了多less次DecimalToBase方法,Stringbuilder对象只被初始化一次。 如果你做了这个更改3以上将不再工作。
我希望有人发现这个有用的:)
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()); }
我用这个来存储Guid作为一个较短的string(但限于使用106个字符)。 如果有人在这里感兴趣的是我的代码解码string回到数值(在这种情况下,我使用Guid值2 ulongs,而不是编码Int128(因为我在3.5不是4.0)。为清楚起见,CODE是string常量与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); } }
我也有类似的需求,除了我需要对“数字”进行math运算。 我在这里提出了一些build议,并创build了一个能够完成所有这些有趣的事情的课程。 它允许任何unicode字符被用来表示一个数字,它也可以使用小数。
这个class很容易使用。 只需要创build一个New BaseNumber
types的New BaseNumber
,设置一些属性,然后closures。 这个例程会自动地在base 10和base x之间切换,并且你设置的值被保存在你设置的base中,所以不会丢失精度(直到转换,但是即使这样,精度损失也应该是非常小的,因为这个例程尽可能使用Double
和Long
)。
我无法控制这个程序的速度。 这可能是相当缓慢的,所以我不确定它是否会适合提问者的需求,但肯定是灵活的,所以希望别人可以使用这个。
对于任何可能需要此代码来计算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在重新编号中由@标识。 所以我只是筛选出所有有@的数字,并得到正确的序列(A,B,C,…,Z,AA,AB,AC …)。
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)); } } }