Cách nhanh nhất để chuyển đổi một số cơ sở 10 sang bất kỳ cơ số nào trong .NET?


107

Tôi có và phương thức C # cũ (ish) mà tôi đã viết nhận một số và chuyển đổi nó thành bất kỳ cơ số nào:

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

Nó không phải là tất cả những gì siêu tốc độ và gọn gàng. Có cách nào hay, đã biết để đạt được điều này trong .NET không?

Tôi đang tìm thứ gì đó cho phép tôi sử dụng bất kỳ cơ sở nào với một chuỗi ký tự tùy ý để sử dụng.

Điều này chỉ cho phép các cơ sở 16, 10, 8 và 2:

Convert.ToString(1, x);

Tôi muốn sử dụng điều này để đạt được cơ số lớn tận dụng các số, tất cả các chữ thường và tất cả các chữ hoa. Giống như trong chủ đề này , nhưng đối với C # không phải JavaScript.

Có ai biết về một cách tốt và hiệu quả để làm điều này trong C # không?

Câu trả lời:


135

Convert.ToString có thể được sử dụng để chuyển đổi một số thành biểu diễn chuỗi tương đương của nó trong một cơ sở xác định.

Thí dụ:

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

Tuy nhiên, như được chỉ ra bởi các nhận xét, Convert.ToStringchỉ hỗ trợ tập hợp các cơ sở hạn chế - nhưng thường là đủ - sau: 2, 8, 10 hoặc 16.

Cập nhật (để đáp ứng yêu cầu chuyển đổi sang bất kỳ cơ sở nào):

Tôi không biết bất kỳ phương pháp nào trong BCL có khả năng chuyển đổi số thành bất kỳ cơ số nào, vì vậy bạn sẽ phải viết hàm tiện ích nhỏ của riêng mình. Một mẫu đơn giản sẽ giống như vậy (lưu ý rằng điều này chắc chắn có thể được thực hiện nhanh hơn bằng cách thay thế nối chuỗi):

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

Cập nhật 2 (Cải thiện hiệu suất)

Sử dụng bộ đệm mảng thay vì nối chuỗi để xây dựng chuỗi kết quả giúp cải thiện hiệu suất, đặc biệt là trên số lượng lớn (xem phương pháp IntToStringFast). Trong trường hợp tốt nhất (tức là đầu vào dài nhất có thể), phương pháp này nhanh hơn khoảng ba lần. Tuy nhiên, đối với các số có 1 chữ số (tức là 1 chữ số trong cơ sở mục tiêu), IntToStringsẽ nhanh hơn.


5
Cần lưu ý rằng điều này chỉ hỗ trợ các cơ sở 2,8,10,16 - không phải là "bất kỳ" trong câu hỏi. 'vì bạn không bao giờ biết khi nào bạn sẽ cần đến số đo giới tính ;-p
Marc Gravell

46
Sexagesimal nghe có vẻ thú vị.
kiềm chế

Với targetBase là 60 và giá trị là 12345, Dòng này trong phương thức IntToString: value = value / targetBase; sẽ làm cho giá trị = 203.75. Điều này có chính xác? Nó không nên giữ nó như một số nguyên?
Adam Harte

6
Tuyệt vời. Nhưng đâu là hàm nghịch đảo? : /
ashes999

2
Tôi có một hàm nghịch đảo chuyển đầu tiên ở đây: stackoverflow.com/questions/3579970/…
ashes999,

78

Gần đây tôi đã viết blog về điều này . Việc triển khai của tôi không sử dụng bất kỳ hoạt động chuỗi nào trong khi tính toán, điều này làm cho nó rất nhanh . Hỗ trợ chuyển đổi sang bất kỳ hệ thống chữ số nào có cơ số từ 2 đến 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;
}

Tôi cũng đã triển khai một chức năng nghịch đảo nhanh trong trường hợp bất kỳ ai cũng cần nó: Hệ thống chữ số thập phân tùy ý .


5
Tôi đã thử nghiệm hoàn hảo tất cả các giải pháp trên trang này và đây là giải pháp nhanh nhất, nhanh gấp đôi so với giải pháp ngắn gọn ở cuối.
Justin R.

Cái gì thế này result = "-" + result? Đó là một số loại đệm? Làm cách nào để sửa đổi mã để tôi chỉ sử dụng AZ hoặc 0-9 cho một ký tự đệm?
oscilatingcretin

Các "-"trong result = "-" + resultlà viết tắt cho các dấu hiệu tiêu cực của số âm. Nó không phải là một ký tự đệm.
Pavel Vladov

2
Tại sao đây không phải là câu trả lời được chấp nhận? Thật tuyệt vời!
Avrohom Yisroel

Cảm ơn bạn, điều này đã tiết kiệm cho tôi rất nhiều thời gian.
NinjaLlama

15

PHƯƠNG THỨC " TỪ " VÀ " ĐẾN " NHANH

Tôi đến muộn với bữa tiệc, nhưng tôi đã tổng hợp các câu trả lời trước đó và cải thiện chúng. Tôi nghĩ rằng hai phương pháp này nhanh hơn bất kỳ phương pháp nào khác được đăng cho đến nay. Tôi đã có thể chuyển đổi 1.000.000 số từ và sang cơ số 36 trong thời gian dưới 400ms trong một máy lõi đơn.

Ví dụ dưới đây dành cho cơ số 62 . Thay đổi BaseCharsmảng để chuyển đổi từ và sang bất kỳ cơ sở nào khác.

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

CHỈNH SỬA (2018-07-12)

Đã sửa lỗi để giải quyết trường hợp góc được tìm thấy bởi @AdrianBotor (xem nhận xét) chuyển đổi 46655 thành cơ số 36. Điều này gây ra bởi một lỗi dấu phẩy động nhỏ khi tính toán Math.Log(46656, 36)chính xác là 3, nhưng .NET trả về 3 + 4.44e-16, gây ra một ký tự thừa trong bộ đệm đầu ra .


@AdrianBotor Không thể repro vấn đề của bạn:BaseToLong(LongToBase(46655)) == 46655
Diego

2
@Diego, Xin lỗi vì đã trả lời muộn. Hãy khởi tạo mảng với 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZvà chuyển đổi giá trị 46655. Kết quả nên có ZZZnhưng trong trình gỡ lỗi tôi nhận được \0ZZZ. Chỉ giá trị này được bổ sung \0. Ví dụ giá trị 46654chuyển đổi đúng thành ZZY.
Adrian Botor

@AdrianBotor Bắt tốt. Khắc phục bằng cách điều chỉnh tuyên bố trở lại LongToBasethànhreturn new string(buffer, (int) i, buffer.Length - (int)i);
Diego

7

Người ta cũng có thể sử dụng phiên bản được sửa đổi một chút của phiên bản được chấp nhận và điều chỉnh chuỗi ký tự cơ sở theo nhu cầu của nó:

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

Rất muộn để đến bữa tiệc về điều này, nhưng tôi đã viết lớp trợ giúp sau đây gần đây cho một dự án tại nơi làm việc. Nó được thiết kế để chuyển đổi các chuỗi ngắn thành số và quay lại (một hàm băm hoàn hảo đơn giản ), tuy nhiên nó cũng sẽ thực hiện chuyển đổi số giữa các cơ số tùy ý. Việc Base10ToStringtriển khai phương pháp trả lời câu hỏi đã được đăng ban đầu.

Các shouldSupportRoundTrippinglá cờ truyền cho constructor lớp là cần thiết để ngăn chặn sự mất mát của các chữ số hàng đầu từ các chuỗi số trong chuyển đổi sang cơ số 10 và ngược lại (rất quan trọng, do yêu cầu của tôi!). Hầu hết thời gian, việc mất các số 0 đứng đầu trong chuỗi số có thể không phải là vấn đề.

Dù sao, đây là mã:

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

Điều này cũng có thể được phân lớp để lấy các bộ chuyển đổi số tùy chỉnh:

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

Và mã sẽ được sử dụng như thế này:

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

Lớp học này từ bài đăng trên diễn đàn này có thể giúp gì cho bạn?

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; 

} 

}

Hoàn toàn chưa được kiểm tra ... hãy cho tôi biết nếu nó hoạt động! (Sao chép-dán nó trong trường hợp bài đăng trên diễn đàn bị biến mất hoặc cái gì đó ...)


Đóng .. Tôi sẽ chơi sau. Sẽ cần một chút công việc để có thể lấy bất kỳ ký tự nào, nhưng đó là một bước đi đúng hướng. Tôi sẽ so sánh tốc độ với phương pháp của riêng tôi!
joshcomley

Hãy nhớ chia sẻ nó nếu ở đây nếu bạn cải thiện nó. Ai đó cũng có thể muốn ot =)
Svish

@joshcomley Cuối tuần thế nào? ;)
Mikkel R. Lund

3
Đó là một ngày cuối tuần dài: D
joshcomley

1

Tôi cũng đang tìm kiếm một cách nhanh chóng để chuyển đổi số thập phân sang một cơ số khác trong phạm vi [2..36] vì vậy tôi đã phát triển đoạn mã sau. Đơn giản để theo dõi và sử dụng đối tượng Stringbuilder làm proxy cho bộ đệm ký tự mà chúng ta có thể lập chỉ mục từng ký tự. Mã có vẻ rất nhanh so với các lựa chọn thay thế và nhanh hơn rất nhiều so với việc khởi tạo các ký tự riêng lẻ trong một mảng ký tự.

Để sử dụng cho riêng bạn, bạn có thể thích: 1 / Trả về một chuỗi trống hơn là ném một ngoại lệ. 2 / loại bỏ kiểm tra cơ số để làm cho phương thức chạy nhanh hơn 3 / Khởi tạo đối tượng Stringbuilder với 32 '0 và loại bỏ kết quả dòng.Remove (0, i) ;. Điều này sẽ làm cho chuỗi được trả về với các số không ở đầu và tăng tốc độ hơn nữa. 4 / Làm cho đối tượng Stringbuilder trở thành một trường tĩnh trong lớp để cho dù phương thức DecimalToBase được gọi là bao nhiêu lần thì đối tượng Stringbuilder cũng chỉ được khởi tạo một lần. Nếu bạn làm điều này, thay đổi 3 ở trên sẽ không còn hoạt động.

Tôi hi vọng ai đó thấy nó hữu ích :)

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

Tôi đang sử dụng điều này để lưu trữ Hướng dẫn dưới dạng chuỗi ngắn hơn (nhưng bị giới hạn sử dụng 106 ký tự). Nếu có ai quan tâm thì đây là mã của tôi để giải mã chuỗi trở lại giá trị số (trong trường hợp này, tôi đã sử dụng 2 ulongs cho giá trị Guid, thay vì mã hóa Int128 (vì tôi đang ở 3.5 không phải 4.0). Để rõ ràng, CODE là một chuỗi const với 106 ký tự duy nhất. ConvertLongsToBytes khá thú vị.

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

Tôi cũng có nhu cầu tương tự, ngoại trừ việc tôi cũng cần làm toán về các "con số". Tôi đã lấy một số gợi ý ở đây và tạo một lớp học sẽ thực hiện tất cả những điều thú vị này. Nó cho phép bất kỳ ký tự unicode nào được sử dụng để đại diện cho một số và nó cũng hoạt động với các số thập phân.

Lớp này khá dễ sử dụng. Chỉ cần tạo một số dưới dạng một loại New BaseNumber, đặt một vài thuộc tính và tắt của bạn. Các quy trình sẽ tự động xử lý việc chuyển đổi giữa cơ số 10 và cơ số x và giá trị bạn đặt được giữ nguyên trong cơ sở bạn đã đặt nó, do đó, không có độ chính xác nào bị mất (cho đến khi chuyển đổi, nhưng ngay cả khi đó mất độ chính xác sẽ rất nhỏ vì điều này sử dụng thường xuyên DoubleLongnếu có thể).

Tôi không thể chỉ huy tốc độ của quy trình này. Nó có lẽ khá chậm, vì vậy tôi không chắc liệu nó có phù hợp với nhu cầu của người đặt câu hỏi hay không, nhưng nó chắc chắn là linh hoạt, vì vậy hy vọng ai đó có thể sử dụng nó.

Đối với bất kỳ ai khác có thể cần mã này để tính toán cột tiếp theo trong Excel, tôi sẽ bao gồm mã lặp mà tôi đã sử dụng để thúc đẩy lớp này.

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

Và bây giờ để mã lặp qua các cột 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

Bạn sẽ lưu ý rằng phần quan trọng của phần Excel là 0 được xác định bằng dấu @ trong số dựa trên lại. Vì vậy, tôi chỉ cần lọc ra tất cả các số có dấu @ và tôi nhận được dãy thích hợp (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));


       }
    }
}

Đó là cho các số cơ bản từ 1 đến 10.
Martin Dimitrov

0

Nếu bất kỳ ai đang tìm kiếm một tùy chọn VB, điều này dựa trên câu trả lời của 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

Đây là một cách khá đơn giản để làm điều này, nhưng nó có thể không phải là nhanh nhất. Nó khá mạnh mẽ vì nó có thể kết hợp được.

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

Kết hợp điều này với phương pháp mở rộng đơn giản này và mọi việc lấy bất kỳ cơ sở nào hiện có thể thực hiện được:

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

Nó có thể được sử dụng như thế này:

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

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

Đầu ra là:

10111
11X
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.