Cara tercepat untuk mengonversi bilangan basis 10 menjadi basis apa pun di .NET?

107

Saya memiliki dan metode C # lama (ish) yang saya tulis yang mengambil angka dan mengubahnya menjadi basis apa pun:

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

Tidak semuanya super cepat dan rapi. Apakah ada cara yang baik dan diketahui untuk mencapai hal ini di .NET?

Saya mencari sesuatu yang memungkinkan saya menggunakan basis apa pun dengan string karakter yang berubah-ubah untuk digunakan.

Ini hanya mengizinkan basis 16, 10, 8 dan 2:

Convert.ToString(1, x);

Saya ingin menggunakan ini untuk mencapai basis yang sangat tinggi dengan memanfaatkan angka, semua huruf kecil dan semua huruf besar. Seperti di utas ini , tetapi untuk C # bukan JavaScript.

Adakah yang tahu cara yang baik dan efisien untuk melakukan ini di C #?

joshcomley
sumber

Jawaban:

135

Convert.ToString dapat digunakan untuk mengubah angka menjadi representasi string ekuivalennya dalam basis yang ditentukan.

Contoh:

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

Namun, seperti yang ditunjukkan oleh komentar, Convert.ToStringhanya mendukung set basis terbatas berikut - tetapi biasanya cukup -: 2, 8, 10, atau 16.

Perbarui (untuk memenuhi persyaratan untuk mengonversi ke basis mana pun):

Saya tidak mengetahui metode apa pun di BCL yang mampu mengonversi angka menjadi basis apa pun sehingga Anda harus menulis fungsi utilitas kecil Anda sendiri. Contoh sederhana akan terlihat seperti itu (perhatikan bahwa ini pasti dapat dibuat lebih cepat dengan mengganti rangkaian string):

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

Perbarui 2 (Peningkatan Kinerja)

Menggunakan buffer array sebagai ganti penggabungan string untuk membangun string hasil memberikan peningkatan kinerja terutama pada jumlah yang besar (lihat metode IntToStringFast). Dalam kasus terbaik (yaitu masukan terpanjang mungkin) metode ini kira-kira tiga kali lebih cepat. Namun, untuk angka 1 digit (yaitu 1 digit di basis target), IntToStringakan lebih cepat.

Dirk Vollmar
sumber
5
Perlu dicatat bahwa ini hanya mendukung basis 2,8,10,16 - bukan "apapun" dalam pertanyaan. karena Anda tidak pernah tahu kapan Anda akan membutuhkan sexagesimal ;-p
Marc Gravell
46
Seksagesimal terdengar menyenangkan.
kendalikan
Dengan targetBase 60, dan nilai 12345, Baris ini dalam metode IntToString: value = value / targetBase; akan membuat nilai = 203.75. Apakah ini benar? Bukankah seharusnya disimpan sebagai bilangan bulat?
Adam Harte
6
Hebat. Tapi dimana fungsi inversnya? : /
ashes999
2
Saya memiliki fungsi pembalikan lintasan pertama di sini: stackoverflow.com/questions/3579970/…
ashes999
78

Saya baru-baru ini membuat blog tentang ini . Implementasi saya tidak menggunakan operasi string apa pun selama kalkulasi, yang membuatnya sangat cepat . Konversi ke sistem angka apa pun dengan basis dari 2 hingga 36 didukung:

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

Saya juga telah mengimplementasikan fungsi invers cepat jika ada yang membutuhkannya juga: Sewaktu-waktu ke Sistem Angka Desimal .

Pavel Vladov
sumber
5
Saya telah menguji semua solusi di halaman ini, dan ini adalah yang tercepat, sekitar dua kali lebih cepat dari solusi singkat di bagian akhir.
Justin R.
Apa ini result = "-" + result? Apakah itu semacam bantalan? Bagaimana saya bisa mengubah kode sehingga saya hanya menggunakan AZ atau 0-9 untuk karakter padding?
oscilatingcretin
The "-"di result = "-" + resulttribun untuk tanda negatif dari angka negatif. Ini bukan karakter padding.
Pavel Vladov
2
Mengapa ini bukan jawaban yang diterima? Itu brilian!
Avrohom Yisroel
Terima kasih, ini menghemat banyak waktu saya.
NinjaLlama
15

METODE " DARI " DAN " KE " CEPAT

Saya terlambat ke pesta, tetapi saya menggabungkan jawaban sebelumnya dan memperbaikinya. Saya pikir kedua metode ini lebih cepat daripada yang lain yang diposting sejauh ini. Saya dapat mengubah 1.000.000 angka dari dan ke basis 36 di bawah 400ms dalam satu mesin inti.

Contoh di bawah ini untuk basis 62 . Ubah BaseCharslarik yang akan diubah dari dan ke basis lainnya.

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

EDIT (2018-07-12)

Memperbaiki untuk mengatasi kasus sudut yang ditemukan oleh @AdrianBotor (lihat komentar) yang mengubah 46655 menjadi basis 36. Ini disebabkan oleh kesalahan titik mengambang kecil Math.Log(46656, 36)yang menghitung persis 3, tetapi .NET kembali 3 + 4.44e-16, yang menyebabkan karakter tambahan dalam buffer keluaran .

Diego
sumber
@AdrianBotor Tidak dapat memperbaiki masalah Anda:BaseToLong(LongToBase(46655)) == 46655
Diego
2
@Diego, Maaf terlambat membalas. Mari kita inisialisasi array dengan 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZdan mengonversi nilai 46655. Hasilnya harus ZZZtetapi di debugger yang saya dapatkan \0ZZZ. Hanya nilai ini yang mendapat tambahan \0. Misalnya nilai 46654diubah dengan benar menjadi ZZY.
Adrian Botor
@Adrianotor Tangkapan bagus. Diatasi dengan mengutak-atik pernyataan pengembalian LongToBasekereturn new string(buffer, (int) i, buffer.Length - (int)i);
Diego
7

Seseorang juga dapat menggunakan versi yang sedikit dimodifikasi dari yang diterima dan menyesuaikan string karakter dasar dengan kebutuhannya:

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;
}
Kresimir
sumber
4

Sangat terlambat ke pesta yang satu ini, tetapi saya menulis kelas pembantu berikut baru-baru ini untuk sebuah proyek di tempat kerja. Ini dirancang untuk mengubah string pendek menjadi angka dan kembali lagi ( fungsi hash sempurna yang sederhana ), namun juga akan melakukan konversi angka antara basis arbitrer. The Base10ToStringmetode pelaksanaan menjawab pertanyaan yang awalnya diumumkan.

The shouldSupportRoundTrippingbendera dilewatkan ke konstruktor kelas diperlukan untuk mencegah hilangnya terkemuka digit dari jumlah string yang selama konversi ke basis-10 dan kembali lagi (penting, mengingat kebutuhan saya!). Sebagian besar waktu hilangnya 0 di depan dari string angka mungkin tidak akan menjadi masalah.

Bagaimanapun, ini kodenya:

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

Ini juga dapat disubkelas untuk mendapatkan konverter nomor kustom:

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

Dan kodenya akan digunakan seperti ini:

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);
            }
        }
    }
}
Steve Rands
sumber
2

Bisakah kelas dari postingan forum ini membantu Anda?

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; 

} 

}

Benar-benar belum teruji ... beri tahu saya jika berhasil! (Salin-tempel jika posting forum hilang atau sesuatu ...)

Svish
sumber
Tutup .. Aku akan bermain nanti. Ini akan membutuhkan sedikit usaha untuk dapat mengambil karakter apa pun, tetapi ini adalah langkah ke arah yang benar. Saya akan membandingkan kecepatan dengan metode saya sendiri!
joshcomley
Ingatlah untuk membagikannya jika di sini jika Anda memperbaikinya. Orang lain mungkin juga ingin =)
Svish
@joshcomley Bagaimana akhir pekan ini? ;)
Mikkel R. Lund
3
Itu adalah akhir pekan yang panjang: D
joshcomley
1

Saya juga sedang mencari cara cepat untuk mengubah angka desimal ke basis lain dalam kisaran [2..36] jadi saya mengembangkan kode berikut. Ini mudah diikuti dan menggunakan objek Stringbuilder sebagai proxy untuk buffer karakter yang dapat kita indeks karakter demi karakter. Kode tersebut tampaknya sangat cepat dibandingkan dengan alternatif dan jauh lebih cepat daripada menginisialisasi karakter individu dalam larik karakter.

Untuk penggunaan Anda sendiri, Anda mungkin memilih untuk: 1 / Mengembalikan string kosong daripada membuat pengecualian. 2 / hapus pemeriksaan radix untuk membuat metode berjalan lebih cepat 3 / Inisialisasi objek Stringbuilder dengan 32 '0 dan hapus hasil baris. Hapus (0, i) ;. Ini akan menyebabkan string dikembalikan dengan angka nol di depan dan selanjutnya meningkatkan kecepatan. 4 / Jadikan objek Stringbuilder sebagai bidang statis di dalam kelas sehingga tidak peduli berapa kali metode DecimalToBase dipanggil, objek Stringbuilder hanya dijalankan sekali. Jika Anda melakukan ini, perubahan 3 di atas tidak akan berfungsi lagi.

Saya harap seseorang menemukan ini berguna :)

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

Saya menggunakan ini untuk menyimpan Guid sebagai string yang lebih pendek (tetapi dibatasi untuk menggunakan 106 karakter). Jika ada yang tertarik di sini adalah kode saya untuk mendekode string kembali ke nilai numerik (dalam hal ini saya menggunakan 2 ulong untuk nilai Guid, daripada mengkodekan Int128 (karena saya di 3.5 bukan 4.0). Untuk kejelasan KODE adalah a string const dengan 106 karakter unik. ConvertLongsToBytes sangat tidak menyenangkan.

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);
        }
    }
Gary
sumber
0

Saya memiliki kebutuhan yang sama, kecuali saya perlu mengerjakan matematika pada "angka" juga. Saya mengambil beberapa saran di sini dan membuat kelas yang akan melakukan semua hal menyenangkan ini. Ini memungkinkan karakter unicode apa pun untuk digunakan untuk mewakili angka dan berfungsi dengan desimal juga.

Kelas ini cukup mudah digunakan. Buat saja nomor sebagai jenis New BaseNumber, setel beberapa properti, dan nonaktifkan Anda. Rutinitas menangani peralihan antara basis 10 dan basis x secara otomatis dan nilai yang Anda tetapkan dipertahankan di basis yang Anda tetapkan, jadi tidak ada akurasi yang hilang (hingga konversi itu terjadi, tetapi bahkan kehilangan presisi harus sangat minimal karena ini penggunaan rutin DoubledanLong jika memungkinkan).

Saya tidak bisa memerintahkan kecepatan rutinitas ini. Mungkin agak lambat, jadi saya tidak yakin apakah itu akan sesuai dengan kebutuhan orang yang bertanya, tapi pasti fleksibel, jadi mudah-mudahan orang lain bisa menggunakan ini.

Bagi siapa pun yang mungkin memerlukan kode ini untuk menghitung kolom berikutnya di Excel, saya akan menyertakan kode perulangan yang saya gunakan yang memanfaatkan kelas ini.

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

Dan sekarang untuk kode untuk mengulang melalui kolom 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

Anda akan melihat bagian penting dari bagian Excel adalah bahwa 0 diidentifikasi oleh @ di nomor berbasis ulang. Jadi saya hanya menyaring semua angka yang memiliki @ di dalamnya dan saya mendapatkan urutan yang tepat (A, B, C, ..., Z, AA, AB, AC, ...).

cjbarth.dll
sumber
0
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));


       }
    }
}
Martin Dimitrov
sumber
Itu untuk nomor dasar dari 1 sampai 10.
Martin Dimitrov
0

Jika ada yang mencari opsi VB, ini didasarkan pada jawaban Pavel:

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
Caius Jard
sumber
0

Ini adalah cara yang cukup mudah untuk melakukan ini, tetapi ini mungkin bukan yang tercepat. Ini cukup kuat karena dapat disusun.

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

Gabungkan ini dengan metode ekstensi sederhana ini dan mendapatkan basis apa pun sekarang dimungkinkan:

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

Ini bisa digunakan seperti ini:

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

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

Outputnya adalah:

10111
11X
Enigmativitas
sumber