أسرع طريقة لتحويل رقم 10 قاعدة إلى أي قاعدة في .NET؟
-
06-09-2019 - |
سؤال
لدي وسيلة قديمة (ISH) C # كتبت أن تأخذ رقما وتحولها إلى أي قاعدة:
string ConvertToBase(int number, char[] baseChars);
ليس كل ما يسرع به السوبر وأنيق. هل هناك طريقة جيدة معروفة لتحقيق هذا في .NET؟
أنا أبحث عن شيء يسمح لي باستخدام أي قاعدة مع سلسلة تعسفية من الشخصيات لاستخدامها.
هذا يسمح فقط القواعد 16 و 10 و 8 و 2:
Convert.ToString(1, x);
أريد استخدام هذا لتحقيق قاعدة عالية على نطاق واسع الاستفادة من الأرقام، وجميع الأحرف الصغيرة وجميع الأحرف الكبيرة. كما هو الحال في هذا الموضوع, ، ولكن ل C # وليس جافا سكريبت.
هل يعرف أحد وسيلة جيدة وفعالة للقيام بذلك في C #؟
المحلول
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 (IE 1 رقمين في القاعدة المستهدفة)، IntToString
سيكون أسرع.
نصائح أخرى
لقد بلوق مؤخرا عن هذا. وبعد لا يستخدم تطبيقي أي عمليات سلسلة أثناء الحسابات، مما يجعلها سريع جدا. وبعد يتم دعم التحويل إلى أي نظام للأرقام مع قاعدة من 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;
}
لقد قمت أيضا بتنفيذ وظيفة معكوس سريعة في حالة احتاج أي شخص أيضا:تعسفي لنظام الأرقام العشرية.
بسرعة "من" و "ل" طرق
أتأخر عن الحفلة، لكنني تضيع إجابات سابقة وتحسينها. أعتقد أن هاتين الطريقتين أسرع من أي شيء آخر نشر حتى الآن. تمكنت من تحويل أرقام 1،000،000 من وإلى قاعدة 36 في أقل من 400ms في جهاز أساسي واحد.
مثال أدناه هو ل قاعدة 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
, ، مما يؤدي إلى حرف إضافي في المخزن المؤقت الإخراج.
يمكن للمرء أيضا استخدام نسخة معدلة قليلا من واحدة مقبولة وضبط سلسلة الأحرف الأساسية لاحتياجاتها:
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;
}
في وقت متأخر جدا للحفلة على هذا واحد، لكنني كتبت الفئة المساعد التالية مؤخرا لمشروع في العمل. تم تصميمه لتحويل الأوتار القصيرة إلى أرقام والعودة مرة أخرى (تبسيط الكمال التجزئة وظيفة)، ومع ذلك سوف تؤدي أيضا تحويل عدد بين القواعد التعسفية. ال Base10ToString
يجيب تنفيذ الطريقة على السؤال الذي تم نشره في الأصل.
ال shouldSupportRoundTripping
هناك حاجة إلى Flag مرت إلى منشئ الطبقة لمنع فقدان الأرقام الرائدة من سلسلة الأرقام أثناء التحويل إلى Base-10 والعودة مرة أخرى (حاسمة، بالنظر إلى متطلباتي!). معظم الوقت لن يكون فقدان فقدان الرائدة من سلسلة الأرقام مشكلة.
على أي حال، إليك الرمز:
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 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;
}
}
لم تختبر تماما ... اسمحوا لي أن أعرف إذا كان يعمل! (نسخ لصقها في حالة تطور منشور المنتدى أو شيء ما ...)
كنت أيضا أبحث عن طريقة سريعة لتحويل الرقم العشري إلى قاعدة أخرى في نطاق [2..36] لذلك قمت بتطوير التعليمات البرمجية التالية. من السهل اتباع وتستخدم كائن StringBuilder كوكيل لعامل مؤقت حرف يمكن أن نقوم بفهرسة الحرف حسب الحرف. يبدو أن الرمز سريعا جدا مقارنة بالبدائل وأسرع بكثير من تهيئة الأحرف الفردية في صفيف حرف.
بالنسبة للاستخدام الخاص بك، قد تفضل: 1 / إرجاع سلسلة فارغة بدلا من رمي استثناء. 2 / قم بإزالة فحص Radix لجعل الطريقة تعمل حتى بشكل أسرع / تهيئة كائن StringBuilder مع 32 '0's وإزالة نتيجة السطر. Remove (0، I)؛ سيؤدي ذلك إلى إرجاع السلسلة مع الأصفار الرائدة وزيادة السرعة. 4 / قم بإجراء كائن StringBuilder حقل ثابت داخل الفصل، لذا بغض النظر عما زمن عدد المرات التي تسمى طريقة 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 كسلسلة أقصر (ولكن كان محدودا لاستخدام 106 حرفا). إذا كان أي شخص مهتم هنا هو رمز فك تشفير السلسلة مرة أخرى إلى القيمة الرقمية (في هذه الحالة، استخدمت 2 Ulongs لقيمة GUID، بدلا من الترميز ANT128 (لأنني في 3.5 وليس 4.0). للحصول على رمز الوضوح سلسلة const مع 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);
}
}
كان لدي حاجة مماثلة، إلا أنني بحاجة إلى القيام بالرياضيات على "الأرقام" أيضا. أخذت بعض الاقتراحات هنا وخلق فئة ستفعل كل هذه الأشياء الممتعة. يسمح بأي حرف Unicode لتمثيل رقم ويعمل مع الكسور العشرية أيضا.
هذه الفئة سهلة الاستخدام للغاية. فقط إنشاء رقم كنوع من New BaseNumber
, ، تعيين عدد قليل من الخصائص، وإيقاف التشغيل. الروتين العناية بالتبديل بين الأساس 10 والقاعدة X تلقائيا ويتم الحفاظ على القيمة التي تحددها في الأساس، لذلك لا تضيع دقة (حتى التحويل، ولكن حتى الخسارة الدقيقة يجب أن تكون ضئيلة للغاية منذ ذلك الحين الاستخدامات الروتينية 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 @ في عدد إعادة تعيينه. لذلك أنا مجرد تصفية جميع الأرقام التي تحتوي على @ لهم وأحصل على التسلسل المناسب (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));
}
}
}