Maneira mais rápida de converter um número de base 10 em qualquer base do .NET?

107

Eu tenho um método C # antigo (ish) que escrevi que pega um número e o converte em qualquer base:

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

Não é tão rápido e organizado. Existe uma maneira boa e conhecida de fazer isso no .NET?

Estou procurando algo que me permita usar qualquer base com uma seqüência arbitrária de caracteres para usar.

Isso permite apenas as bases 16, 10, 8 e 2:

Convert.ToString(1, x);

Quero usar isso para obter uma base muito alta, aproveitando os números, todos em minúsculas e todas em maiúsculas. Como neste tópico , mas para C # não para JavaScript.

Alguém conhece uma maneira boa e eficiente de fazer isso em C #?

Joshcomley
fonte

Respostas:

135

Convert.ToString pode ser usado para converter um número em sua representação de string equivalente em uma base especificada.

Exemplo:

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

No entanto, como apontado pelos comentários, Convert.ToStringsuporta apenas o seguinte conjunto limitado - mas normalmente suficiente - de bases: 2, 8, 10 ou 16.

Atualização (para atender ao requisito de conversão para qualquer base):

Não estou ciente de nenhum método no BCL que seja capaz de converter números em qualquer base, então você teria que escrever sua própria função de utilidade pequena. Um exemplo simples ficaria assim (observe que isso certamente pode ser feito mais rápido substituindo a concatenação de 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);
    }
}

Atualização 2 (melhoria de desempenho)

Usar um buffer de array em vez de concatenação de string para construir a string de resultado fornece uma melhoria de desempenho, especialmente em grande número (consulte o método IntToStringFast). No melhor caso (ou seja, a entrada mais longa possível), esse método é cerca de três vezes mais rápido. No entanto, para números de 1 dígito (ou seja, 1 dígito na base de destino), IntToStringserá mais rápido.

Dirk Vollmar
fonte
5
Deve-se notar que este suporta apenas bases 2,8,10,16 - não o "qualquer" em questão. porque você nunca sabe quando vai precisar de sexagesimal ;-p
Marc Gravell
45
Sexagesimal parece divertido.
reinará
Com targetBase de 60 e valor de 12345, esta linha no método IntToString: value = value / targetBase; faria o valor = 203,75. Isso está correto? Não deveria mantê-lo como um número inteiro?
Adam Harte
6
Impressionante. Mas onde está a função inversa? : /
ashes999
2
Eu tenho uma função inversa de primeira passagem aqui: stackoverflow.com/questions/3579970/…
ashes999
78

Recentemente, escrevi sobre isso . Minha implementação não usa nenhuma operação de string durante os cálculos, o que a torna muito rápida . A conversão para qualquer sistema numérico com base de 2 a 36 é suportada:

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

Também implementei uma função inversa rápida caso alguém também precise: Sistema de Numeral Arbitrário para Decimal .

Pavel Vladov
fonte
5
Testei perfeitamente todas as soluções desta página, e esta é a mais rápida, cerca de duas vezes mais rápida que a solução curta no final.
Justin R.
O que é isso result = "-" + result? Isso é algum tipo de enchimento? Como posso modificar o código para usar apenas AZ ou 0-9 para um caractere de preenchimento?
oscilatingcretin
O "-"in result = "-" + resultrepresenta o sinal negativo de números negativos. Não é um caractere de preenchimento.
Pavel Vladov
2
Por que essa não é a resposta aceita? É brilhante!
Avrohom Yisroel
Obrigado, isso me economizou muito tempo.
NinjaLlama
15

MÉTODOS " DE " E " PARA " RÁPIDO

Estou atrasado para a festa, mas compus respostas anteriores e as melhorei. Acho que esses dois métodos são mais rápidos do que qualquer outro postado até agora. Consegui converter 1.000.000 de números de e para a base 36 em menos de 400 ms em uma máquina de núcleo único.

O exemplo abaixo é para a base 62 . Altere a BaseCharsmatriz para converter de e para qualquer outra 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;  
} 

EDIT (12/07/2018)

Corrigido para resolver o caso de canto encontrado por @AdrianBotor (ver comentários) convertendo 46655 para a base 36. Isso é causado por um pequeno erro de ponto flutuante calculando Math.Log(46656, 36)que é exatamente 3, mas o .NET retorna 3 + 4.44e-16, o que causa um caractere extra no buffer de saída .

Diego
fonte
@AdrianBotor Não é possível reproduzir seu problema:BaseToLong(LongToBase(46655)) == 46655
Diego,
2
@Diego, Desculpe pelo atraso na resposta. Vamos inicializar o array 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZe converter o valor 46655. O resultado deve ser, ZZZmas no depurador que recebo \0ZZZ. Apenas este valor é adicional \0. Por exemplo, o valor 46654converte corretamente para ZZY.
Adrian Botor,
@AdrianBotor Boa captura. Dirigida por ajustes a instrução de retorno em LongToBasequereturn new string(buffer, (int) i, buffer.Length - (int)i);
Diego
7

Também é possível usar uma versão ligeiramente modificada da aceita e ajustar a sequência de caracteres de base de acordo com suas necessidades:

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

Muito tarde para a festa neste aqui, mas escrevi a seguinte classe auxiliar recentemente para um projeto no trabalho. Ele foi projetado para converter strings curtas em números e vice-versa (uma função hash perfeita simplista ), no entanto, também realizará a conversão de números entre bases arbitrárias. A Base10ToStringimplementação do método responde à pergunta que foi postada originalmente.

O shouldSupportRoundTrippingsinalizador passado para o construtor da classe é necessário para evitar a perda dos dígitos iniciais da string numérica durante a conversão para a base 10 e vice-versa (crucial, dados meus requisitos!). Na maioria das vezes, a perda dos 0s iniciais da sequência de números provavelmente não será um problema.

Enfim, aqui está o 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;
        }
    }
}

Isso também pode ser uma subclasse para derivar conversores 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)
        {
        }
    }
}

E o código seria usado assim:

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

Esta classe desta postagem do fórum pode ajudá-lo?

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 não testado ... deixe-me saber se funciona! (Copie e cole no caso de a postagem no fórum desaparecer ou algo assim ...)

Svish
fonte
Fechar .. Vou jogar mais tarde. Vai precisar de um pouco de trabalho para conseguir pegar qualquer caractere, mas é um passo na direção certa. Vou comparar a velocidade com meu próprio método!
joshcomley
Lembre-se de compartilhá-lo aqui se você melhorar. Outra pessoa pode querer também =)
Svish
@joshcomley Como foi o fim de semana? ;)
Mikkel R. Lund
3
Foi um fim de semana prolongado: D
joshcomley
1

Eu também estava procurando uma maneira rápida de converter o número decimal em outra base no intervalo de [2..36], então desenvolvi o código a seguir. É simples de seguir e usa um objeto Stringbuilder como proxy para um buffer de caracteres que podemos indexar caractere por caractere. O código parece ser muito rápido em comparação com as alternativas e muito mais rápido do que inicializar caracteres individuais em uma matriz de caracteres.

Para seu próprio uso, você pode preferir: 1 / Retornar uma string em branco em vez de lançar uma exceção. 2 / remova a verificação de raiz para fazer o método rodar ainda mais rápido 3 / Inicialize o objeto Stringbuilder com 32 '0's e remova o resultado da linha.Remove (0, i) ;. Isso fará com que a string seja retornada com zeros à esquerda e aumentará ainda mais a velocidade. 4 / Faça do objeto Stringbuilder um campo estático dentro da classe, portanto, não importa quantas vezes o método DecimalToBase seja chamado, o objeto Stringbuilder só é inicializado uma vez. Se você fizer essa alteração, 3 acima não funcionará mais.

Espero que alguém ache isso ú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());
    }
user1031307
fonte
0

Eu estava usando isso para armazenar um Guid como uma string mais curta (mas estava limitado a usar 106 caracteres). Se alguém estiver interessado, aqui está o meu código para decodificar a string de volta para um valor numérico (neste caso, usei 2 ulongs para o valor Guid, em vez de codificar um Int128 (já que estou no 3.5 e não no 4.0). Para maior clareza, CODE é um string const com 106 caracteres exclusivos. ConvertLongsToBytes é bastante monótono.

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

Eu tinha uma necessidade semelhante, exceto que precisava fazer matemática sobre os "números" também. Peguei algumas das sugestões aqui e criei uma classe que vai fazer toda essa diversão. Ele permite que qualquer caractere Unicode seja usado para representar um número e também funciona com decimais.

Esta classe é muito fácil de usar. Basta criar um número como um tipo de New BaseNumber, definir algumas propriedades e desligar. As rotinas se encarregam de alternar entre a base 10 e a base x automaticamente e o valor que você define é preservado na base que você define, então nenhuma precisão é perdida (até a conversão, mas mesmo assim a perda de precisão deve ser mínima, pois isso usos de rotina Doublee Longsempre que possível).

Não posso controlar a velocidade dessa rotina. Provavelmente é muito lento, então não tenho certeza se atenderá às necessidades de quem fez a pergunta, mas é flexível, então espero que outra pessoa possa usar isso.

Para qualquer outra pessoa que possa precisar desse código para calcular a próxima coluna no Excel, incluirei o código de loop que usei para aproveitar essa classe.

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

E agora, para o código percorrer as colunas do 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

Você notará que a parte importante da parte do Excel é que 0 é identificado por um @ no número refeito. Então, eu apenas filtro todos os números que têm um @ neles e obtenho a sequência adequada (A, B, C, ..., Z, AA, AB, AC, ...).

cjbarth
fonte
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
fonte
Isso é para os números básicos de 1 a 10.
Martin Dimitrov
0

Se alguém está procurando uma opção VB, isso foi baseado na resposta 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
Caius Jard
fonte
0

Esta é uma maneira bastante direta de fazer isso, mas pode não ser a mais rápida. É muito poderoso porque pode ser composto.

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 isso com este método de extensão simples e qualquer obtenção de base agora é possível:

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

Ele pode ser usado assim:

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

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

O resultado é:

10111
11X
Enigmatividade
fonte