.NET에서 10 진수를 임의의 염기로 변환하는 가장 빠른 방법은 무엇입니까?


107

나는 숫자를 취하여 어떤 염기로도 변환하는 이전 (ish) C # 메소드를 가지고 있습니다.

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

그다지 빠르고 깔끔한 것은 아닙니다. .NET에서 이것을 달성하는 잘 알려진 방법이 있습니까?

사용할 임의의 문자열로 모든 기본 을 사용할 수있는 것을 찾고 있습니다.

이것은 16, 10, 8 및 2 염기 만 허용합니다.

Convert.ToString(1, x);

나는 이것을 사용하여 숫자, 모두 소문자 및 모두 대문자를 활용하여 엄청나게 높은 기수를 얻고 싶습니다. 에서와 마찬가지로 이 스레드 ,하지만 C #을하지 자바 스크립트.

누구든지 C #에서 이것을 수행하는 훌륭하고 효율적인 방법을 알고 있습니까?

답변:


135

Convert.ToString 숫자를 지정된 밑수의 해당 문자열 표현으로 변환하는 데 사용할 수 있습니다.

예:

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

그러나 주석에서 지적한대로 Convert.ToString에서는 제한적이지만 일반적으로 충분한 기준 세트 인 2, 8, 10 또는 16 만 지원합니다.

업데이트 (기본으로 변환하기위한 요구 사항 충족) :

나는 숫자를 어떤 밑으로도 변환 할 수있는 BCL의 어떤 방법도 알지 못하기 때문에 자신 만의 작은 유틸리티 함수를 작성해야합니다. 간단한 샘플은 다음과 같습니다 (문자열 연결을 바꾸면 더 빨리 만들 수 있습니다).

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

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

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

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

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

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

        return result;
    }

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

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

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

        return new string(result);
    }
}

업데이트 2 (성능 개선)

문자열 연결 대신 배열 버퍼를 사용하여 결과 문자열을 작성하면 특히 많은 수에서 성능이 향상됩니다 (method 참조 IntToStringFast). 가장 좋은 경우 (즉, 가능한 가장 긴 입력)이 방법은 대략 3 배 더 빠릅니다. 그러나 1 자리 숫자 (즉, 대상 기준의 1 자리 숫자)의 IntToString경우 더 빠릅니다.


5
이것은 2,8,10,16 염기 만 지원한다는 점에 유의해야합니다. 질문의 "any"가 아닙니다. 60 진수가 언제 필요할지 모르기 때문에 ;-p
Marc Gravell

46
60 진수는 재미있을 것 같습니다.
고삐

targetBase가 60이고 값이 12345 인 IntToString 메서드의이 줄 : value = value / targetBase; 값 = 203.75가됩니다. 이 올바른지? 정수로 유지해야하지 않습니까?
Adam Harte 2010-07-28

6
대박. 그러나 역함수는 어디에 있습니까? : /
ashes999 2010-08-26

2
여기에 첫 번째 통과 역함수가 있습니다. stackoverflow.com/questions/3579970/…
ashes999

78

나는 최근에 이것에 대해 블로그에 올렸다 . 내 구현은 계산 중에 문자열 연산을 사용하지 않으므로 매우 빠릅니다 . 2에서 36까지의 기수가있는 모든 숫자 체계로의 변환이 지원됩니다.

/// <summary>
/// Converts the given decimal number to the numeral system with the
/// specified radix (in the range [2, 36]).
/// </summary>
/// <param name="decimalNumber">The number to convert.</param>
/// <param name="radix">The radix of the destination numeral system (in the range [2, 36]).</param>
/// <returns></returns>
public static string DecimalToArbitrarySystem(long decimalNumber, int radix)
{
    const int BitsInLong = 64;
    const string Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    if (radix < 2 || radix > Digits.Length)
        throw new ArgumentException("The radix must be >= 2 and <= " + Digits.Length.ToString());

    if (decimalNumber == 0)
        return "0";

    int index = BitsInLong - 1;
    long currentNumber = Math.Abs(decimalNumber);
    char[] charArray = new char[BitsInLong];

    while (currentNumber != 0)
    {
        int remainder = (int)(currentNumber % radix);
        charArray[index--] = Digits[remainder];
        currentNumber = currentNumber / radix;
    }

    string result = new String(charArray, index + 1, BitsInLong - index - 1);
    if (decimalNumber < 0)
    {
        result = "-" + result;
    }

    return result;
}

누구에게나 필요한 경우를 대비하여 빠른 역함수도 구현했습니다 : Arbitrary to Decimal Numeral System .


5
나는이 페이지에있는 모든 솔루션을 테스트했는데 이것은 마지막에있는 짧은 솔루션보다 약 두 배 빠른 속도입니다.
Justin R.

이것은 무엇입니까 result = "-" + result? 그것은 일종의 패딩입니까? 패딩 문자에 AZ 또는 0-9 만 사용하도록 코드를 수정하려면 어떻게해야합니까?
oscilatingcretin

"-"에서 result = "-" + result음수의 마이너스 기호를 의미합니다. 패딩 문자가 아닙니다.
Pavel Vladov

2
왜 이것이 허용되지 않는 대답입니까? 훌륭 해요!
Avrohom Yisroel

감사합니다. 이로 인해 많은 시간이 절약되었습니다.
NinjaLlama

15

FAST " FROM AND" " TO "방법

나는 파티에 늦었지만 이전 답변을 합성하고 개선했습니다. 이 두 가지 방법이 지금까지 게시 된 다른 방법보다 빠르다고 생각합니다. 단일 코어 머신에서 400ms 이내에 1,000,000 개의 숫자를 기본 36으로 변환 할 수있었습니다.

아래의 예는 기본 62 입니다. BaseChars다른 염기로 변환 하려면 배열을 변경하십시오 .

private static readonly char[] BaseChars = 
         "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
private static readonly Dictionary<char, int> CharValues = BaseChars
           .Select((c,i)=>new {Char=c, Index=i})
           .ToDictionary(c=>c.Char,c=>c.Index);

public static string LongToBase(long value)
{
   long targetBase = BaseChars.Length;
   // Determine exact number of characters to use.
   char[] buffer = new char[Math.Max( 
              (int) Math.Ceiling(Math.Log(value + 1, targetBase)), 1)];

   var i = buffer.Length;
   do
   {
       buffer[--i] = BaseChars[value % targetBase];
       value = value / targetBase;
   }
   while (value > 0);

   return new string(buffer, i, buffer.Length - i);
}

public static long BaseToLong(string number) 
{ 
    char[] chrs = number.ToCharArray(); 
    int m = chrs.Length - 1; 
    int n = BaseChars.Length, x;
    long result = 0; 
    for (int i = 0; i < chrs.Length; i++)
    {
        x = CharValues[ chrs[i] ];
        result += x * (long)Math.Pow(n, m--);
    }
    return result;  
} 

수정 (2018-07-12)

46655를 기본 36으로 변환하는 @AdrianBotor (주석 참조)에서 발견 된 코너 케이스를 해결하도록 수정되었습니다. 이는 Math.Log(46656, 36)정확히 3 인 계산의 작은 부동 소수점 오류 3 + 4.44e-16로 인해 발생 하지만 .NET은을 반환 하여 출력 버퍼에 추가 문자가 발생합니다. .


@AdrianBotor 문제를 재현 할 수 없습니다 :BaseToLong(LongToBase(46655)) == 46655
Diego

2
@Diego, 늦게 답장해서 죄송합니다. 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZvalue로 배열을 초기화 하고 변환 해 봅시다 46655. 결과는 있어야 ZZZ하지만 디버거에서는 \0ZZZ. 이 값만 추가 \0됩니다. 예를 들어 값 46654ZZY.
Adrian Botor 2017-10-09

@AdrianBotor 잘 잡았습니다. 에서 return 문을 조정에 의해 해결 LongToBasereturn new string(buffer, (int) i, buffer.Length - (int)i);
디에고

7

허용되는 버전의 약간 수정 된 버전을 사용하고 필요에 따라 기본 문자열을 조정할 수도 있습니다.

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

이것에 대한 파티에 매우 늦었지만 최근에 직장에서 프로젝트를 위해 다음 도우미 클래스를 작성했습니다. 짧은 문자열을 숫자로 다시 변환하도록 설계 되었지만 (단순한 완벽한 해시 함수) 임의의 염기간에 숫자 변환도 수행합니다. Base10ToString메소드 구현은 원래 게시 된 질문에 대한 대답.

shouldSupportRoundTripping클래스 생성자에 전달 된 플래그는 10 진수로 변환하는 동안 숫자 문자열에서 선행 자릿수가 손실되는 것을 방지하기 위해 필요합니다 (내 요구 사항에 따라 중요합니다!). 대부분의 경우 숫자 문자열에서 선행 0이 손실되는 것은 문제가되지 않을 것입니다.

어쨌든 다음은 코드입니다.

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

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

        public Base10Converter(string digits, bool shouldSupportRoundTripping = false)
            : this(digits.ToCharArray(), shouldSupportRoundTripping)
        {
        }

        public Base10Converter(IEnumerable<char> digits, bool shouldSupportRoundTripping = false)
        {
            if (digits == null)
            {
                throw new ArgumentNullException("digits");
            }

            if (digits.Count() == 0)
            {
                throw new ArgumentException(
                    message: "The sequence is empty.",
                    paramName: "digits"
                    );
            }

            if (!digits.Distinct().SequenceEqual(digits))
            {
                throw new ArgumentException(
                    message: "There are duplicate characters in the sequence.",
                    paramName: "digits"
                    );
            }

            if (shouldSupportRoundTripping)
            {
                digits = (new[] { NullDigit }).Concat(digits);
            }

            _digitToIndexMap =
                digits
                .Select((digit, index) => new { digit, index })
                .ToDictionary(keySelector: x => x.digit, elementSelector: x => x.index);

            _radix = _digitToIndexMap.Count;

            _indexToDigitMap =
                _digitToIndexMap
                .ToDictionary(keySelector: x => x.Value, elementSelector: x => x.Key);
        }

        readonly Dictionary<char, int> _digitToIndexMap;
        readonly Dictionary<int, char> _indexToDigitMap;
        readonly int _radix;

        public long StringToBase10(string number)
        {
            Func<char, int, long> selector =
                (c, i) =>
                {
                    int power = number.Length - i - 1;

                    int digitIndex;
                    if (!_digitToIndexMap.TryGetValue(c, out digitIndex))
                    {
                        throw new ArgumentException(
                            message: String.Format("Number contains an invalid digit '{0}' at position {1}.", c, i),
                            paramName: "number"
                            );
                    }

                    return Convert.ToInt64(digitIndex * Math.Pow(_radix, power));
                };

            return number.Select(selector).Sum();
        }

        public string Base10ToString(long number)
        {
            if (number < 0)
            {
                throw new ArgumentOutOfRangeException(
                    message: "Value cannot be negative.",
                    paramName: "number"
                    );
            }

            string text = string.Empty;

            long remainder;
            do
            {
                number = Math.DivRem(number, _radix, out remainder);

                char digit;
                if (!_indexToDigitMap.TryGetValue((int) remainder, out digit) || digit == NullDigit)
                {
                    throw new ArgumentException(
                        message: "Value cannot be converted given the set of digits used by this converter.",
                        paramName: "number"
                        );
                }

                text = digit + text;
            }
            while (number > 0);

            return text;
        }
    }
}

이것은 또한 사용자 지정 숫자 변환기를 파생하기 위해 하위 분류 될 수 있습니다.

namespace StackOverflow
{
    public sealed class BinaryNumberConverter : Base10Converter
    {
        public BinaryNumberConverter()
            : base(digits: "01", shouldSupportRoundTripping: false)
        {
        }
    }

    public sealed class HexNumberConverter : Base10Converter
    {
        public HexNumberConverter()
            : base(digits: "0123456789ABCDEF", shouldSupportRoundTripping: false)
        {
        }
    }
}

그리고 코드는 다음과 같이 사용됩니다.

using System.Diagnostics;

namespace StackOverflow
{
    class Program
    {
        static void Main(string[] args)
        {
            {
                var converter = new Base10Converter(
                    digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz",
                    shouldSupportRoundTripping: true
                    );

                long number = converter.StringToBase10("Atoz");
                string text = converter.Base10ToString(number);
                Debug.Assert(text == "Atoz");
            }

            {
                var converter = new HexNumberConverter();

                string text = converter.Base10ToString(255);
                long number = converter.StringToBase10(text);
                Debug.Assert(number == 255);
            }
        }
    }
}

2

이 포럼 게시물 의이 수업 도움 될 수 있습니까?

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; 

} 

}

완전히 테스트되지 않았습니다 ... 작동하는지 알려주세요! (포럼 게시물이 사라지는 경우를 대비하여 복사-붙여 넣기 ...)


닫기 .. 나중에 연극 할게. 모든 문자를 가져 오려면 약간의 작업이 필요하지만 올바른 방향으로 나아가는 단계입니다. 속도를 내 방식과 비교해 볼게요!
joshcomley

개선하면 여기에 공유하는 것을 잊지 마십시오. 다른 사람도 원할 수도 있습니다 =)
Svish

@joshcomley 주말은 어땠어? ;)
Mikkel R. Lund

3
긴 주말이었다 : D
joshcomley

1

나도 십진수를 [2..36] 범위의 다른 밑으로 변환하는 빠른 방법을 찾고 있었기 때문에 다음 코드를 개발했습니다. 따라 가기 쉽고 Stringbuilder 객체를 문자별로 인덱싱 할 수있는 문자 버퍼의 프록시로 사용합니다. 코드는 대안에 비해 매우 빠르며 문자 배열에서 개별 문자를 초기화하는 것보다 훨씬 빠릅니다.

자신의 사용을 위해 다음을 선호 할 수 있습니다. 1 / 예외를 던지기보다는 빈 문자열을 반환합니다. 2 / 메서드가 더 빠르게 실행되도록 기수 검사를 제거합니다. 3 / 32 '0'으로 Stringbuilder 객체를 초기화하고 result.Remove (0, i); 줄을 제거합니다. 이렇게하면 문자열이 선행 0으로 반환되고 속도가 더욱 빨라집니다. 4 / Stringbuilder 객체를 클래스 내에서 정적 필드로 만들어 DecimalToBase 메서드가 몇 번 호출 되든 Stringbuilder 객체는 한 번만 초기화됩니다. 이 변경을 수행하면 위의 3이 더 이상 작동하지 않습니다.

누군가 이것이 유용하다고 생각하기를 바랍니다. :)

AtomicParadox

        static string DecimalToBase(int number, int radix)
    {
        // Check that the radix is between 2 and 36 inclusive
        if ( radix < 2 || radix > 36 )
            throw new ArgumentException("ConvertToBase(int number, int radix) - Radix must be between 2 and 36.");

        // Create a buffer large enough to hold the largest int value represented in binary digits 
        StringBuilder result = new StringBuilder("                                ");  // 32 spaces

        // The base conversion calculates the digits in reverse order so use
        // an index to point to the last unused space in our buffer
        int i = 32; 

        // Convert the number to the new base
        do
        {
            int remainder = number % radix;
            number = number / radix;
            if(remainder <= 9)
                result[--i] = (char)(remainder + '0');  // Converts [0..9] to ASCII ['0'..'9']
            else
                result[--i] = (char)(remainder + '7');  // Converts [10..36] to ASCII ['A'..'Z']
        } while ( number > 0 );

        // Remove the unwanted padding from the front of our buffer and return the result
        // Note i points to the last unused character in our buffer
        result.Remove( 0, i );
        return (result.ToString());
    }

0

나는 이것을 사용하여 Guid를 더 짧은 문자열로 저장했습니다 (그러나 106 자로 제한되었습니다). 여기에 관심이있는 사람은 문자열을 다시 숫자 값으로 디코딩하는 코드입니다 (이 경우에는 Int128을 코딩하는 대신 Guid 값에 2 ulong을 사용했습니다 (4.0이 아닌 3.5에 있으므로). 명확성을 위해 CODE는 106 개의 고유 문자를 가진 문자열 const.ConvertLongsToBytes는 꽤 흥미롭지 않습니다.

private static Guid B106ToGuid(string pStr)
    {
        try
        {
            ulong tMutl = 1, tL1 = 0, tL2 = 0, targetBase = (ulong)CODE.Length;
            for (int i = 0; i < pStr.Length / 2; i++)
            {
                tL1 += (ulong)CODE.IndexOf(pStr[i]) * tMutl;
                tL2 += (ulong)CODE.IndexOf(pStr[pStr.Length / 2 + i]) * tMutl;
                tMutl *= targetBase;
            }
            return new Guid(ConvertLongsToBytes(tL1, tL2));
        }
        catch (Exception ex)
        {
            throw new Exception("B106ToGuid failed to convert string to Guid", ex);
        }
    }

0

나는 "숫자"에 대해서도 수학을해야한다는 것을 제외하고는 비슷한 필요가 있었다. 여기에서 몇 가지 제안을 받아이 모든 재미있는 일을 할 수있는 수업을 만들었습니다. 유니 코드 문자를 사용하여 숫자를 나타내는 데 사용할 수 있으며 소수에서도 작동합니다.

이 클래스는 사용하기 매우 쉽습니다. 유형으로 번호를 만들고 New BaseNumber몇 가지 속성을 설정 한 다음 해제하면됩니다. 루틴은 기본 10과 기본 x 사이를 자동으로 전환하며 설정 한 값은 설정 한 기본에 보존되므로 정확도가 손실되지 않습니다 (즉, 변환 할 때까지는 정밀도 손실이 매우 미미해야합니다. 일상적인 사용 DoubleLong가능한 경우).

이 루틴의 속도를 명령 할 수 없습니다. 아마도 꽤 느리기 때문에 질문을 한 사람의 요구에 맞는지 확실하지 않지만 확실히 유연하므로 다른 사람이 이것을 사용할 수 있기를 바랍니다.

Excel에서 다음 열을 계산하는 데이 코드가 필요한 다른 사람을 위해이 클래스를 활용하는 루핑 코드를 포함하겠습니다.

Public Class BaseNumber

    Private _CharacterArray As List(Of Char)

    Private _BaseXNumber As String
    Private _Base10Number As Double?

    Private NumberBaseLow As Integer
    Private NumberBaseHigh As Integer

    Private DecimalSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator
    Private GroupSeparator As Char = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator

    Public Sub UseCapsLetters()
        'http://unicodelookup.com
        TrySetBaseSet(65, 90)
    End Sub

    Public Function GetCharacterArray() As List(Of Char)
        Return _CharacterArray
    End Function

    Public Sub SetCharacterArray(CharacterArray As String)
        _CharacterArray = New List(Of Char)
        _CharacterArray.AddRange(CharacterArray.ToList)

        TrySetBaseSet(_CharacterArray)
    End Sub

    Public Sub SetCharacterArray(CharacterArray As List(Of Char))
        _CharacterArray = CharacterArray
        TrySetBaseSet(_CharacterArray)
    End Sub

    Public Sub SetNumber(Value As String)
        _BaseXNumber = Value
        _Base10Number = Nothing
    End Sub

    Public Sub SetNumber(Value As Double)
        _Base10Number = Value
        _BaseXNumber = Nothing
    End Sub

    Public Function GetBaseXNumber() As String
        If _BaseXNumber IsNot Nothing Then
            Return _BaseXNumber
        Else
            Return ToBaseString()
        End If
    End Function

    Public Function GetBase10Number() As Double
        If _Base10Number IsNot Nothing Then
            Return _Base10Number
        Else
            Return ToBase10()
        End If
    End Function

    Private Sub TrySetBaseSet(Values As List(Of Char))
        For Each value As Char In _BaseXNumber
            If Not Values.Contains(value) Then
                Throw New ArgumentOutOfRangeException("The string has a value, " & value & ", not contained in the selected 'base' set.")
                _CharacterArray.Clear()
                DetermineNumberBase()
            End If
        Next

        _CharacterArray = Values

    End Sub

    Private Sub TrySetBaseSet(LowValue As Integer, HighValue As Integer)

        Dim HighLow As KeyValuePair(Of Integer, Integer) = GetHighLow()

        If HighLow.Key < LowValue OrElse HighLow.Value > HighValue Then
            Throw New ArgumentOutOfRangeException("The string has a value not contained in the selected 'base' set.")
            _CharacterArray.Clear()
            DetermineNumberBase()
        End If

        NumberBaseLow = LowValue
        NumberBaseHigh = HighValue

    End Sub

    Private Function GetHighLow(Optional Values As List(Of Char) = Nothing) As KeyValuePair(Of Integer, Integer)
        If Values Is Nothing Then
            Values = _BaseXNumber.ToList
        End If

        Dim lowestValue As Integer = Convert.ToInt32(Values(0))
        Dim highestValue As Integer = Convert.ToInt32(Values(0))

        Dim currentValue As Integer

        For Each value As Char In Values

            If value <> DecimalSeparator AndAlso value <> GroupSeparator Then
                currentValue = Convert.ToInt32(value)
                If currentValue > highestValue Then
                    highestValue = currentValue
                End If
                If currentValue < lowestValue Then
                    currentValue = lowestValue
                End If
            End If
        Next

        Return New KeyValuePair(Of Integer, Integer)(lowestValue, highestValue)

    End Function

    Public Sub New(BaseXNumber As String)
        _BaseXNumber = BaseXNumber
        DetermineNumberBase()
    End Sub

    Public Sub New(BaseXNumber As String, NumberBase As Integer)
        Me.New(BaseXNumber, Convert.ToInt32("0"c), NumberBase)
    End Sub

    Public Sub New(BaseXNumber As String, NumberBaseLow As Integer, NumberBaseHigh As Integer)
        _BaseXNumber = BaseXNumber
        Me.NumberBaseLow = NumberBaseLow
        Me.NumberBaseHigh = NumberBaseHigh
    End Sub

    Public Sub New(Base10Number As Double)
        _Base10Number = Base10Number
    End Sub

    Private Sub DetermineNumberBase()
        Dim highestValue As Integer

        Dim currentValue As Integer

        For Each value As Char In _BaseXNumber

            currentValue = Convert.ToInt32(value)
            If currentValue > highestValue Then
                highestValue = currentValue
            End If
        Next

        NumberBaseHigh = highestValue
        NumberBaseLow = Convert.ToInt32("0"c) 'assume 0 is the lowest

    End Sub

    Private Function ToBaseString() As String
        Dim Base10Number As Double = _Base10Number

        Dim intPart As Long = Math.Truncate(Base10Number)
        Dim fracPart As Long = (Base10Number - intPart).ToString.Replace(DecimalSeparator, "")

        Dim intPartString As String = ConvertIntToString(intPart)
        Dim fracPartString As String = If(fracPart <> 0, DecimalSeparator & ConvertIntToString(fracPart), "")

        Return intPartString & fracPartString

    End Function

    Private Function ToBase10() As Double
        Dim intPartString As String = _BaseXNumber.Split(DecimalSeparator)(0).Replace(GroupSeparator, "")
        Dim fracPartString As String = If(_BaseXNumber.Contains(DecimalSeparator), _BaseXNumber.Split(DecimalSeparator)(1), "")

        Dim intPart As Long = ConvertStringToInt(intPartString)
        Dim fracPartNumerator As Long = ConvertStringToInt(fracPartString)
        Dim fracPartDenominator As Long = ConvertStringToInt(GetEncodedChar(1) & String.Join("", Enumerable.Repeat(GetEncodedChar(0), fracPartString.ToString.Length)))

        Return Convert.ToDouble(intPart + fracPartNumerator / fracPartDenominator)

    End Function

    Private Function ConvertIntToString(ValueToConvert As Long) As String
        Dim result As String = String.Empty
        Dim targetBase As Long = GetEncodingCharsLength()

        Do
            result = GetEncodedChar(ValueToConvert Mod targetBase) & result
            ValueToConvert = ValueToConvert \ targetBase
        Loop While ValueToConvert > 0

        Return result
    End Function

    Private Function ConvertStringToInt(ValueToConvert As String) As Long
        Dim result As Long
        Dim targetBase As Integer = GetEncodingCharsLength()
        Dim startBase As Integer = GetEncodingCharsStartBase()

        Dim value As Char
        For x As Integer = 0 To ValueToConvert.Length - 1
            value = ValueToConvert(x)
            result += GetDecodedChar(value) * Convert.ToInt32(Math.Pow(GetEncodingCharsLength, ValueToConvert.Length - (x + 1)))
        Next

        Return result

    End Function

    Private Function GetEncodedChar(index As Integer) As Char
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray(index)
        Else
            Return Convert.ToChar(index + NumberBaseLow)
        End If
    End Function

    Private Function GetDecodedChar(character As Char) As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray.IndexOf(character)
        Else
            Return Convert.ToInt32(character) - NumberBaseLow
        End If
    End Function

    Private Function GetEncodingCharsLength() As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return _CharacterArray.Count
        Else
            Return NumberBaseHigh - NumberBaseLow + 1
        End If
    End Function

    Private Function GetEncodingCharsStartBase() As Integer
        If _CharacterArray IsNot Nothing AndAlso _CharacterArray.Count > 0 Then
            Return GetHighLow.Key
        Else
            Return NumberBaseLow
        End If
    End Function
End Class

이제 코드에서 Excel 열을 반복합니다.

    Public Function GetColumnList(DataSheetID As String) As List(Of String)
        Dim workingColumn As New BaseNumber("A")
        workingColumn.SetCharacterArray("@ABCDEFGHIJKLMNOPQRSTUVWXYZ")

        Dim listOfPopulatedColumns As New List(Of String)
        Dim countOfEmptyColumns As Integer

        Dim colHasData As Boolean
        Dim cellHasData As Boolean

        Do
            colHasData = True
            cellHasData = False
            For r As Integer = 1 To GetMaxRow(DataSheetID)
                cellHasData = cellHasData Or XLGetCellValue(DataSheetID, workingColumn.GetBaseXNumber & r) <> ""
            Next
            colHasData = colHasData And cellHasData

            'keep trying until we get 4 empty columns in a row
            If colHasData Then
                listOfPopulatedColumns.Add(workingColumn.GetBaseXNumber)
                countOfEmptyColumns = 0
            Else
                countOfEmptyColumns += 1
            End If

            'we are already starting with column A, so increment after we check column A
            Do
                workingColumn.SetNumber(workingColumn.GetBase10Number + 1)
            Loop Until Not workingColumn.GetBaseXNumber.Contains("@")

        Loop Until countOfEmptyColumns > 3

        Return listOfPopulatedColumns

    End Function

Excel 부분의 중요한 부분은 다시 기반 번호에서 0이 @로 식별된다는 것입니다. 그래서 저는 @가있는 모든 숫자를 걸러 내고 적절한 시퀀스 (A, 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));


       }
    }
}

기본 숫자의 1에서 10까지 그
마틴 디미트로프

0

누군가 VB 옵션을 찾고 있다면 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

이것은이를 수행하는 매우 간단한 방법이지만 가장 빠르지는 않을 수 있습니다. 구성이 가능하기 때문에 매우 강력합니다.

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

이것을이 간단한 확장 방법과 결합하면 이제 모든베이스를 얻을 수 있습니다.

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

다음과 같이 사용할 수 있습니다.

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

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

출력은 다음과 같습니다.

10111
11X
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.