¿La forma más rápida de convertir un número de base 10 a cualquier base en .NET?


107

Tengo un método C # antiguo (ish) que escribí que toma un número y lo convierte a cualquier base:

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

No es tan rápido y ordenado. ¿Existe una forma buena y conocida de lograr esto en .NET?

Estoy buscando algo que me permita usar cualquier base con una cadena arbitraria de caracteres para usar.

Esto solo permite las bases 16, 10, 8 y 2:

Convert.ToString(1, x);

Quiero usar esto para lograr una base enormemente alta aprovechando los números, todas en minúsculas y todas en mayúsculas. Como en este hilo , pero para C # no JavaScript.

¿Alguien sabe de una forma buena y eficiente de hacer esto en C #?

Respuestas:


135

Convert.ToString se puede utilizar para convertir un número en su representación de cadena equivalente en una base específica.

Ejemplo:

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

Sin embargo, como se señala en los comentarios, Convert.ToStringsolo admite el siguiente conjunto de bases limitado, pero generalmente suficiente: 2, 8, 10 o 16.

Actualización (para cumplir con el requisito de convertir a cualquier base):

No conozco ningún método en el BCL que sea capaz de convertir números a cualquier base, por lo que tendría que escribir su propia función de utilidad pequeña. Una muestra simple se vería así (tenga en cuenta que esto seguramente se puede hacer más rápido reemplazando la concatenación de cadenas):

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

Actualización 2 (mejora del rendimiento)

El uso de un búfer de matriz en lugar de la concatenación de cadenas para construir la cadena de resultado proporciona una mejora del rendimiento, especialmente en números grandes (ver método IntToStringFast). En el mejor de los casos (es decir, la entrada más larga posible), este método es aproximadamente tres veces más rápido. Sin embargo, para números de 1 dígito (es decir, 1 dígito en la base de destino), IntToStringserá más rápido.


5
Cabe señalar que esto solo admite las bases 2, 8, 10, 16, no el "cualquiera" en la pregunta. porque nunca se sabe cuándo necesitará sexagesimal ;-p
Marc Gravell

46
Sexagesimal suena divertido.
reinó

Con un targetBase de 60 y un valor de 12345, esta línea en el método IntToString: value = value / targetBase; haría valor = 203,75. ¿Es esto correcto? ¿No debería mantenerlo como un número entero?
Adam Harte

6
Increíble. Pero, ¿dónde está la función inversa? : /
cenizas999

2
Tengo una función inversa de primer paso aquí: stackoverflow.com/questions/3579970/…
ashes999

78

Recientemente escribí en un blog sobre esto . Mi implementación no utiliza ninguna operación de cadena durante los cálculos, lo que lo hace muy rápido . Se admite la conversión a cualquier sistema numérico con base de 2 a 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;
}

También he implementado una función inversa rápida en caso de que alguien la necesite también: sistema numérico arbitrario a decimal .


5
Probé todas las soluciones en esta página, y esta es la más rápida, aproximadamente el doble de rápido que la solución corta al final.
Justin R.

¿Qué es esto result = "-" + result? ¿Es eso algún tipo de relleno? ¿Cómo podría modificar el código para que solo use AZ o 0-9 para un carácter de relleno?
oscilatingcretin

La "-"en result = "-" + resultrepresenta el signo negativo de los números negativos. No es un carácter de relleno.
Pavel Vladov

2
¿Por qué no es esta la respuesta aceptada? ¡Es brillante!
Avrohom Yisroel

Gracias, esto me ahorró mucho tiempo.
NinjaLlama

15

MÉTODOS RÁPIDOS " DE " Y " A "

Llego tarde a la fiesta, pero combiné las respuestas anteriores y las mejoré. Creo que estos dos métodos son más rápidos que cualquier otro publicado hasta ahora. Pude convertir 1,000,000 de números desde y hacia la base 36 en menos de 400ms en una máquina de un solo núcleo.

El siguiente ejemplo es para la base 62 . Cambie la BaseCharsmatriz para convertir desde y hacia cualquier otra base.

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

EDITAR (2018-07-12)

Se corrigió para abordar el caso de esquina encontrado por @AdrianBotor (ver comentarios) convirtiendo 46655 a base 36. Esto es causado por un pequeño error de punto flotante al calcular Math.Log(46656, 36)cuál es exactamente 3, pero .NET regresa 3 + 4.44e-16, lo que causa un carácter adicional en el búfer de salida .


@AdrianBotor No puede reproducir su problema:BaseToLong(LongToBase(46655)) == 46655
Diego

2
@Diego, lo siento por la respuesta tardía. Inicialicemos la matriz con 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZy convierta el valor 46655. El resultado debería ser, ZZZpero en el depurador obtengo \0ZZZ. Solo este valor se vuelve adicional \0. Por ejemplo, el valor se 46654convierte correctamente en ZZY.
Adrian Botor

@AdrianBotor Buena captura. Dirigido por ajustar la instrucción de retorno en LongToBaseareturn new string(buffer, (int) i, buffer.Length - (int)i);
Diego

7

También se puede usar una versión ligeramente modificada de la aceptada y ajustar la cadena de caracteres base a sus necesidades:

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

4

Muy tarde para la fiesta en este caso, pero recientemente escribí la siguiente clase de ayuda para un proyecto en el trabajo. Fue diseñado para convertir cadenas cortas en números y viceversa (una función de hash perfecta simplista ), sin embargo, también realizará la conversión de números entre bases arbitrarias. La Base10ToStringimplementación del método responde a la pregunta que se publicó originalmente.

La shouldSupportRoundTrippingbandera que se pasa al constructor de la clase es necesaria para evitar la pérdida de los dígitos iniciales de la cadena numérica durante la conversión a base 10 y viceversa (¡crucial, dados mis requisitos!). La mayoría de las veces, la pérdida de ceros iniciales de la cadena de números probablemente no será un problema.

De todos modos, aquí está el código:

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

Esto también se puede subclasificar para derivar convertidores de números personalizados:

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

Y el código se usaría así:

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

2

¿Podría ayudarte esta clase de esta publicación en el foro ?

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; 

} 

}

Totalmente no probado ... ¡avísame si funciona! (Copie y pegue en caso de que la publicación del foro desaparezca o algo así ...)


Cerca ... tendré una obra de teatro más tarde. Necesitará un poco de trabajo para poder tomar cualquier carácter, pero es un paso en la dirección correcta. ¡Compararé la velocidad con mi propio método!
Joshcomley

Recuerda compartirlo aquí si lo mejoras. Alguien más podría quererlo también =)
Svish

@joshcomley ¿Cómo fue el fin de semana? ;)
Mikkel R. Lund

3
Fue un fin de semana largo: D
joshcomley

1

Yo también estaba buscando una forma rápida de convertir un número decimal a otra base en el rango de [2..36], así que desarrollé el siguiente código. Es simple de seguir y usa un objeto Stringbuilder como un proxy para un búfer de caracteres que podemos indexar carácter por carácter. El código parece ser muy rápido en comparación con las alternativas y mucho más rápido que inicializar caracteres individuales en una matriz de caracteres.

Para su propio uso, es posible que prefiera: 1 / Devolver una cadena en blanco en lugar de lanzar una excepción. 2 / elimine la comprobación de radix para que el método se ejecute aún más rápido 3 / Inicialice el objeto Stringbuilder con 32 '0's y elimine la línea result.Remove (0, i) ;. Esto hará que la cadena se devuelva con ceros a la izquierda y aumentará aún más la velocidad. 4 / Haga que el objeto Stringbuilder sea un campo estático dentro de la clase, de modo que no importa cuántas veces se llame al método DecimalToBase, el objeto Stringbuilder solo se inicializa una vez. Si hace este cambio 3 anterior ya no funcionaría.

Espero que alguien encuentre esto útil :)

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

0

Estaba usando esto para almacenar un Guid como una cadena más corta (pero estaba limitado a usar 106 caracteres). Si alguien está interesado, aquí está mi código para decodificar la cadena de nuevo a un valor numérico (en este caso usé 2 ulongs para el valor Guid, en lugar de codificar un Int128 (ya que estoy en 3.5, no 4.0). Para mayor claridad, CODE es un cadena constante con 106 caracteres únicos. ConvertLongsToBytes es bastante aburrido.

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

0

Tenía una necesidad similar, excepto que también necesitaba hacer matemáticas con los "números". Tomé algunas de las sugerencias aquí y creé una clase que hará todas estas cosas divertidas. Permite que se use cualquier carácter Unicode para representar un número y también funciona con decimales.

Esta clase es bastante fácil de usar. Simplemente cree un número como un tipo de New BaseNumber, configure algunas propiedades y listo. Las rutinas se encargan de cambiar entre la base 10 y la base x automáticamente y el valor que establece se conserva en la base en la que lo estableció, por lo que no se pierde precisión (hasta la conversión, es decir, pero incluso entonces la pérdida de precisión debería ser mínima ya que esto usos rutinarios Doubley Longsiempre que sea posible).

No puedo controlar la velocidad de esta rutina. Probablemente sea bastante lento, por lo que no estoy seguro de si se adaptará a las necesidades de quien hizo la pregunta, pero seguro que es flexible, así que espero que alguien más pueda usarlo.

Para cualquier otra persona que pueda necesitar este código para calcular la siguiente columna en Excel, incluiré el código de bucle que usé para aprovechar esta clase.

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

Y ahora para que el código recorra las columnas de 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

Notarás que la parte importante de la parte de Excel es que 0 se identifica con una @ en el número re-basado. Así que solo filtro todos los números que tienen una @ y obtengo la secuencia adecuada (A, B, C, ..., Z, AA, AB, AC, ...).


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


       }
    }
}

Eso es para los números base del 1 al 10.
Martin Dimitrov

0

Si alguien está buscando una opción de VB, esto se basó en la respuesta de 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

0

Esta es una forma bastante sencilla de hacer esto, pero puede que no sea la más rápida. Es bastante poderoso porque se puede componer.

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

Combine esto con este método de extensión simple y ahora es posible obtener cualquier base:

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

Se puede usar así:

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

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

La salida es:

10111
11X
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.