Der schnellste Weg, eine Basis-10-Zahl in eine Basis in .NET zu konvertieren?

107

Ich habe eine alte (ish) C # -Methode, die ich geschrieben habe und die eine Zahl nimmt und in eine beliebige Basis konvertiert:

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

Es ist nicht so super schnell und ordentlich. Gibt es einen guten, bekannten Weg, dies in .NET zu erreichen?

Ich suche nach etwas, mit dem ich jede Basis mit einer beliebigen Zeichenfolge verwenden kann.

Dies erlaubt nur die Basen 16, 10, 8 und 2:

Convert.ToString(1, x);

Ich möchte dies nutzen, um eine massiv hohe Basis zu erreichen, indem ich Zahlen, alle Klein- und Großbuchstaben nutze. Wie in diesem Thread , aber für C # nicht JavaScript.

Kennt jemand eine gute und effiziente Möglichkeit, dies in C # zu tun?

Joshcomley
quelle

Antworten:

135

Convert.ToString kann verwendet werden, um eine Zahl in ihre entsprechende Zeichenfolgendarstellung in einer angegebenen Basis zu konvertieren.

Beispiel:

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

Wie in den Kommentaren hervorgehoben, werden jedoch Convert.ToStringnur die folgenden begrenzten - aber normalerweise ausreichenden - Basissätze unterstützt: 2, 8, 10 oder 16.

Update (um die Anforderung zu erfüllen, auf eine beliebige Basis zu konvertieren):

Mir ist keine Methode in der BCL bekannt, mit der Zahlen in eine beliebige Basis konvertiert werden können, sodass Sie Ihre eigene kleine Dienstprogrammfunktion schreiben müssten. Ein einfaches Beispiel würde so aussehen (beachten Sie, dass dies sicherlich durch Ersetzen der Zeichenfolgenverkettung beschleunigt werden kann):

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

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

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

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

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

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

        return result;
    }

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

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

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

        return new string(result);
    }
}

Update 2 (Leistungsverbesserung)

Die Verwendung eines Array-Puffers anstelle der Verkettung von Zeichenfolgen zum Erstellen der Ergebniszeichenfolge führt zu einer Leistungsverbesserung, insbesondere bei großen Zahlen (siehe Methode IntToStringFast). Im besten Fall (dh bei der längsten möglichen Eingabe) ist diese Methode ungefähr dreimal schneller. Bei 1-stelligen Zahlen (dh 1-stellig in der Zielbasis) ist IntToStringdies jedoch schneller.

Dirk Vollmar
quelle
5
Es sollte beachtet werden, dass dies nur die Basen 2,8,10,16 unterstützt - nicht das "any" in der Frage. Weil du nie weißt, wann du Sexagesimal brauchst ;-p
Marc Gravell
45
Sexagesimal klingt nach Spaß.
Zügel
Mit einer Zielbasis von 60 und einem Wert von 12345 ist diese Zeile in der IntToString-Methode: value = value / targetBase; würde Wert = 203,75 machen. Ist das richtig? Sollte es nicht eine ganze Zahl behalten?
Adam Harte
6
Genial. Aber wo ist die Umkehrfunktion? : /
Asche999
2
Ich habe hier eine Umkehrfunktion für den ersten Durchgang: stackoverflow.com/questions/3579970/…
ashes999
78

Ich habe kürzlich darüber gebloggt . Meine Implementierung verwendet während der Berechnungen keine Zeichenfolgenoperationen, was sie sehr schnell macht . Die Konvertierung in ein beliebiges Zahlensystem mit einer Basis von 2 bis 36 wird unterstützt:

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

Ich habe auch eine schnelle Umkehrfunktion implementiert, falls jemand sie auch benötigt: Arbitrary to Decimal Numeral System .

Pavel Vladov
quelle
5
Ich habe alle Lösungen auf dieser Seite getestet, und dies ist die schnellste, ungefähr doppelt so schnell wie die kurze Lösung am Ende.
Justin R.
Was ist das result = "-" + result? Ist das eine Art Polsterung? Wie kann ich den Code so ändern, dass ich nur AZ oder 0-9 für ein Füllzeichen verwende?
Oscilatingcretin
Das "-"In result = "-" + resultsteht für das negative Vorzeichen negativer Zahlen. Es ist kein Polstercharakter.
Pavel Vladov
2
Warum ist das nicht die akzeptierte Antwort? Es ist brilliant!
Avrohom Yisroel
Danke, das hat mir viel Zeit gespart.
NinjaLlama
15

SCHNELLE " VON " UND " ZU " METHODEN

Ich bin zu spät zur Party, aber ich habe frühere Antworten zusammengesetzt und sie verbessert. Ich denke, diese beiden Methoden sind schneller als alle anderen bisher veröffentlichten. Ich konnte 1.000.000 Zahlen von und zu Basis 36 in weniger als 400 ms in einer Single-Core-Maschine konvertieren.

Das folgende Beispiel gilt für die Basis 62 . Ändern Sie das BaseCharsArray, um es von und zu einer anderen Basis zu konvertieren.

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)

Behoben, um den von @AdrianBotor (siehe Kommentare) gefundenen Eckfall zu beheben, der 46655 in Basis 36 konvertiert. Dies wird durch einen kleinen Gleitkommafehler verursacht, Math.Log(46656, 36)der genau 3 berechnet , aber .NET zurückgibt 3 + 4.44e-16, was ein zusätzliches Zeichen im Ausgabepuffer verursacht .

Diego
quelle
@AdrianBotor Kann Ihr Problem nicht wiederholen:BaseToLong(LongToBase(46655)) == 46655
Diego
2
@Diego, Entschuldigung für die späte Antwort. Initialisieren wir das Array mit 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZund konvertieren den Wert 46655. Ergebnis sollte sein, ZZZaber im Debugger bekomme ich \0ZZZ. Nur dieser Wert wird zusätzlich \0. Zum Beispiel wird der Wert 46654ordnungsgemäß in konvertiert ZZY.
Adrian Botor
@AdrianBotor Guter Fang. Adressiert die return - Anweisung in durch Zwicken LongToBaseanreturn new string(buffer, (int) i, buffer.Length - (int)i);
Diego
7

Sie können auch eine leicht modifizierte Version der akzeptierten Version verwenden und die Zeichenfolge der Basiszeichen an ihre Bedürfnisse anpassen:

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
quelle
4

Sehr spät zur Party, aber ich habe kürzlich die folgende Helferklasse für ein Projekt bei der Arbeit geschrieben. Es wurde entwickelt, um kurze Zeichenfolgen in Zahlen und wieder zurück umzuwandeln (eine vereinfachte, perfekte Hash- Funktion), es wird jedoch auch eine Zahlenumwandlung zwischen beliebigen Basen durchgeführt. Die Base10ToStringMethodenimplementierung beantwortet die ursprünglich gestellte Frage.

Das shouldSupportRoundTrippingan den Klassenkonstruktor übergebene Flag wird benötigt, um den Verlust führender Ziffern aus der Zahlenfolge während der Konvertierung in Basis-10 und wieder zurück zu verhindern (entscheidend angesichts meiner Anforderungen!). Meistens ist der Verlust führender Nullen aus der Zahlenfolge wahrscheinlich kein Problem.

Wie auch immer, hier ist der Code:

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

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

Dies kann auch in Unterklassen unterteilt werden, um benutzerdefinierte Zahlenkonverter abzuleiten:

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

Und der Code würde so verwendet:

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
quelle
2

Könnte Ihnen diese Klasse aus diesem Forumsbeitrag helfen?

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; 

} 

}

Völlig ungetestet ... lass es mich wissen, wenn es funktioniert! (Kopieren Sie es für den Fall, dass der Forumsbeitrag verschwindet oder so ...)

Svish
quelle
Schließen .. Ich werde später ein Stück haben. Es wird ein bisschen Arbeit erfordern, um irgendwelche Zeichen aufnehmen zu können, aber es ist ein Schritt in die richtige Richtung. Ich werde die Geschwindigkeit mit meiner eigenen Methode vergleichen!
Joshcomley
Denken Sie daran, es hier zu teilen, wenn Sie es verbessern. Jemand anderes möchte vielleicht auch nicht =)
Svish
@joshcomley Wie verlief das Wochenende? ;)
Mikkel R. Lund
3
Es war ein langes Wochenende: D
Joshcomley
1

Auch ich suchte nach einer schnellen Möglichkeit, die Dezimalzahl in eine andere Basis im Bereich von [2..36] umzuwandeln, und entwickelte den folgenden Code. Es ist einfach zu befolgen und verwendet ein Stringbuilder-Objekt als Proxy für einen Zeichenpuffer, den wir zeichenweise indizieren können. Der Code scheint im Vergleich zu Alternativen sehr schnell und viel schneller zu sein als das Initialisieren einzelner Zeichen in einem Zeichenarray.

Für Ihren eigenen Gebrauch bevorzugen Sie möglicherweise Folgendes: 1 / Geben Sie eine leere Zeichenfolge zurück, anstatt eine Ausnahme auszulösen. 2 / Entfernen Sie die Radix-Prüfung, damit die Methode noch schneller ausgeführt wird. 3 / Initialisieren Sie das Stringbuilder-Objekt mit 32 '0' und entfernen Sie das Zeilenergebnis. Entfernen Sie (0, i);. Dadurch wird die Zeichenfolge mit führenden Nullen zurückgegeben und die Geschwindigkeit weiter erhöht. 4 / Machen Sie das Stringbuilder-Objekt zu einem statischen Feld innerhalb der Klasse, sodass das Stringbuilder-Objekt unabhängig davon, wie oft die DecimalToBase-Methode aufgerufen wird, nur einmal initialisiert wird. Wenn Sie diese Änderung 3 oben tun, würde nicht mehr funktionieren.

Ich hoffe jemand findet das nützlich :)

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

Ich habe dies verwendet, um eine Guid als kürzere Zeichenfolge zu speichern (war jedoch auf 106 Zeichen beschränkt). Wenn jemand interessiert ist, ist hier mein Code zum Dekodieren der Zeichenfolge zurück in einen numerischen Wert (in diesem Fall habe ich 2 ulongs für den Guid-Wert verwendet, anstatt einen Int128 zu codieren (da ich in 3.5 nicht 4.0 bin). Aus Gründen der Klarheit ist CODE a string const mit 106 eindeutigen Zeichen. ConvertLongsToBytes ist ziemlich aufregend.

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
quelle
0

Ich hatte ein ähnliches Bedürfnis, außer dass ich auch mit den "Zahlen" rechnen musste. Ich habe einige der Vorschläge hier aufgegriffen und eine Klasse erstellt, die all diese lustigen Dinge erledigt. Es ermöglicht die Verwendung eines beliebigen Unicode-Zeichens zur Darstellung einer Zahl und funktioniert auch mit Dezimalstellen.

Diese Klasse ist ziemlich einfach zu bedienen. Erstellen Sie einfach eine Zahl als eine Art von New BaseNumber, legen Sie einige Eigenschaften fest und los geht's. Die Routinen sorgen dafür, dass automatisch zwischen Basis 10 und Basis x umgeschaltet wird und der von Ihnen festgelegte Wert in der Basis erhalten bleibt, in der Sie ihn festgelegt haben, sodass keine Genauigkeit verloren geht (bis zur Konvertierung, aber selbst dann sollte der Genauigkeitsverlust seitdem sehr gering sein Routineverwendungen Doubleund Longwo immer möglich).

Ich kann die Geschwindigkeit dieser Routine nicht bestimmen. Es ist wahrscheinlich ziemlich langsam, daher bin ich mir nicht sicher, ob es den Bedürfnissen desjenigen entspricht, der die Frage gestellt hat, aber es ist sicher flexibel, sodass hoffentlich jemand anderes dies verwenden kann.

Für alle anderen, die diesen Code zur Berechnung der nächsten Spalte in Excel benötigen, werde ich den von mir verwendeten Schleifencode einschließen, der diese Klasse nutzt.

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

Und jetzt, damit der Code die Excel-Spalten durchläuft:

    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

Sie werden feststellen, dass der wichtige Teil des Excel-Teils darin besteht, dass 0 in der neu basierten Nummer durch ein @ gekennzeichnet ist. Also filtere ich einfach alle Zahlen heraus, die ein @ enthalten, und erhalte die richtige Reihenfolge (A, B, C, ..., Z, AA, AB, AC, ...).

cjbarth
quelle
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
quelle
Das ist für die Basisnummern von 1 bis 10.
Martin Dimitrov
0

Wenn jemand eine VB-Option sucht, basiert dies auf Pavel's Antwort:

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
quelle
0

Dies ist ein ziemlich einfacher Weg, aber möglicherweise nicht der schnellste. Es ist ziemlich mächtig, weil es zusammensetzbar ist.

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

Kombinieren Sie dies mit dieser einfachen Erweiterungsmethode und es ist jetzt möglich, eine Basis zu erhalten:

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

Es kann folgendermaßen verwendet werden:

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

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

Die Ausgabe ist:

10111
11X
Rätselhaftigkeit
quelle