Как преобразовать номер столбца (например, 127) в столбец Excel (например, AA)

Как преобразовать числовое число в имя столбца Excel в C# без автоматизации, получая значение непосредственно из Excel.

В Excel 2007 возможный диапазон от 1 до 16384, то есть число поддерживаемых столбцов. Полученные значения должны быть в форме имен столбцов Excel, например, A, AA, AAA и т. Д.

60 ответов

Решение

Вот как я это делаю:

private string GetExcelColumnName(int columnNumber)
{
    int dividend = columnNumber;
    string columnName = String.Empty;
    int modulo;

    while (dividend > 0)
    {
        modulo = (dividend - 1) % 26;
        columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
        dividend = (int)((dividend - modulo) / 26);
    } 

    return columnName;
}

Если кому-то нужно сделать это в Excel без VBA, вот способ:

=SUBSTITUTE(ADDRESS(1;colNum;4);"1";"")

где colNum - номер столбца

И в VBA:

Function GetColumnName(colNum As Integer) As String
    Dim d As Integer
    Dim m As Integer
    Dim name As String
    d = colNum
    name = ""
    Do While (d > 0)
        m = (d - 1) Mod 26
        name = Chr(65 + m) + name
        d = Int((d - m) / 26)
    Loop
    GetColumnName = name
End Function

Извините, это Python вместо C#, но по крайней мере результаты верны:

def ColIdxToXlName(idx):
    if idx < 1:
        raise ValueError("Index is too small")
    result = ""
    while True:
        if idx > 26:
            idx, r = divmod(idx - 1, 26)
            result = chr(r + ord('A')) + result
        else:
            return chr(idx + ord('A') - 1) + result


for i in xrange(1, 1024):
    print "%4d : %s" % (i, ColIdxToXlName(i))

Вам может потребоваться преобразование в обоих направлениях, например, из адреса столбца Excel, такого как AAZ, в целое число и из любого целого числа в Excel. Два метода ниже сделают именно это. При условии индексации на основе 1, первый элемент в ваших "массивах" - это элемент № 1. Здесь нет ограничений по размеру, поэтому вы можете использовать адреса, такие как ОШИБКА, и это будет номер колонки 2613824 ...

public static string ColumnAdress(int col)
{
  if (col <= 26) { 
    return Convert.ToChar(col + 64).ToString();
  }
  int div = col / 26;
  int mod = col % 26;
  if (mod == 0) {mod = 26;div--;}
  return ColumnAdress(div) + ColumnAdress(mod);
}

public static int ColumnNumber(string colAdress)
{
  int[] digits = new int[colAdress.Length];
  for (int i = 0; i < colAdress.Length; ++i)
  {
    digits[i] = Convert.ToInt32(colAdress[i]) - 64;
  }
  int mul=1;int res=0;
  for (int pos = digits.Length - 1; pos >= 0; --pos)
  {
    res += digits[pos] * mul;
    mul *= 26;
  }
  return res;
}

Я обнаружил ошибку в своем первом посте, поэтому я решил сесть и посчитать. Я обнаружил, что система счисления, используемая для идентификации столбцов Excel, не является системой 26, как написал другой человек. Рассмотрим следующее в базе 10. Вы также можете сделать это с буквами алфавита.

Пространство:......................... S1, S2, S3: S1, S2, S3
.................................... 0, 00, 000:.. A, AA, AAA
.................................... 1, 01, 001:.. B, AB, AAB
....................................…,…,…:..…,…,…
.................................... 9, 99, 999:.. Z, ZZ, ZZZ
Всего состояний в космосе: 10, 100, 1000: 26, 676, 17576
Всего штатов:...............1110................18278

Столбцы чисел в Excel в отдельных алфавитных пространствах, используя базу 26. Вы можете видеть, что в общем случае прогрессия пространства состояний представляет собой a, a^2, a^3, … для некоторой базы a, а общее количество состояний равно a + a^2 + a^3 + … .

Предположим, вы хотите найти общее количество состояний A в первых N пробелах. Формула для этого есть A = (a)(a^N - 1)/(a-1). Это важно, потому что нам нужно найти пространство N, которое соответствует нашему индексу K. Если я хочу выяснить, где K лежит в системе счисления, мне нужно заменить A на K и решить для N. Решение N = log{ основание a} (A (a-1)/a +1). Если я использую пример a = 10 и K = 192, я знаю, что N = 2.23804… . Это говорит мне, что K лежит в начале третьего пространства, так как оно немного больше двух.

Следующим шагом будет точное определение того, как далеко мы находимся в текущем пространстве. Чтобы найти это, вычтите из K A, сгенерированное с использованием пола N. В этом примере, пол N равен двум. Итак, A = (10)(10^2 - 1)/(10-1) = 110, как ожидается, когда вы объединяете состояния первых двух пространств. Это нужно вычесть из K, потому что эти первые 110 состояний уже были бы учтены в первых двух пространствах. Это оставляет нас с 82 государствами. Итак, в этой системе счисления представление 192 в базе 10 равно 082.

Код C#, использующий нулевой базовый индекс:

    private string ExcelColumnIndexToName(int Index)
    {
        string range = string.Empty;
        if (Index < 0 ) return range;
        int a = 26;
        int x = (int)Math.Floor(Math.Log((Index) * (a - 1) / a + 1, a));
        Index -= (int)(Math.Pow(a, x) - 1) * a / (a - 1);
        for (int i = x+1; Index + i > 0; i--)
        {
            range = ((char)(65 + Index % a)).ToString() + range;
            Index /= a;
        }
        return range;
    }

// Старый пост

Решение на основе нуля в C#.

    private string ExcelColumnIndexToName(int Index)
    {
        string range = "";
        if (Index < 0 ) return range;
        for(int i=1;Index + i > 0;i=0)
        {
            range = ((char)(65 + Index % 26)).ToString() + range;
            Index /= 26;
        }
        if (range.Length > 1) range = ((char)((int)range[0] - 1)).ToString() + range.Substring(1);
        return range;
    }

Этот ответ в javaScript:

function getCharFromNumber(columnNumber){
    var dividend = columnNumber;
    var columnName = "";
    var modulo;

    while (dividend > 0)
    {
        modulo = (dividend - 1) % 26;
        columnName = String.fromCharCode(65 + modulo).toString() + columnName;
        dividend = parseInt((dividend - modulo) / 26);
    } 
    return  columnName;
}

Хотя уже есть множество правильных ответов 1, ни один из них не вникает в теорию, стоящую за этим.

Имена столбцов Excel являются биективными представлениями их числа по основанию 26. Это сильно отличается от обычного основания 26 (нет начального нуля), и я действительно рекомендую прочитать статью в Википедии , чтобы понять различия. Например, десятичное значение 702(разлагается в 26*26 + 26) представлен в «обычном» основании 26 как 110(т.е. 1x26^2 + 1x26^1 + 0x26^0) и в биективном основании-26 на ZZ(т.е. 26x26^1 + 26x26^0).

Помимо различий, биективная нумерация является позиционной записью , и поэтому мы можем выполнять преобразования с использованием итеративного (или рекурсивного) алгоритма, который на каждой итерации находит цифру следующей позиции (аналогично обычному алгоритму преобразования базы).

Общая формула для получения цифры в последней позиции (с индексом 0) биективного базового представления десятичного числа. mявляется ( fфункция потолка минус 1):

      m - (f(m / k) * k)

Цифра в следующей позиции (то есть с индексом 1) находится путем применения той же формулы к результату . Мы знаем, что для последней цифры (т.е. с наибольшим индексом) f(m / k)равно 0.

Это формирует основу для итерации, которая находит каждую последующую цифру в биективной базе данных. kдесятичного числа. В псевдокоде это будет выглядеть так ( digit()сопоставляет десятичное целое число с его представлением в биективной базе - например digit(1)вернется Aв биективном основании-26):

      fun conv(m) {
    q = f(m / k)
    a = m - (q * k)
    if (q == 0)
        return digit(a)
    else
        return conv(q) + digit(a);

Таким образом, мы можем перевести это на C#2 , чтобы получить общее 3 «преобразование в биективную базу-k» .ToBijective()рутина:

      class BijectiveNumeration {
    private int baseK;
    private Func<int, char> getDigit;
    public BijectiveNumeration(int baseK, Func<int, char> getDigit) {
        this.baseK = baseK;
        this.getDigit = getDigit;
    }

    public string ToBijective(double decimalValue) {
        double q = f(decimalValue / baseK);
        double a = decimalValue - (q * baseK);
        return ((q > 0) ? ToBijective(q) : "") + getDigit((int)a);
    }

    private static double f(double i) {
        return (Math.Ceiling(i) - 1);
    }
}

Теперь для преобразования в биективную базу 26 (наш вариант использования «имя столбца Excel»):

      static void Main(string[] args)
{
    BijectiveNumeration bijBase26 = new BijectiveNumeration(
        26,
        (value) => Convert.ToChar('A' + (value - 1))
    );

    Console.WriteLine(bijBase26.ToBijective(1));     // prints "A"
    Console.WriteLine(bijBase26.ToBijective(26));    // prints "Z"
    Console.WriteLine(bijBase26.ToBijective(27));    // prints "AA"
    Console.WriteLine(bijBase26.ToBijective(702));   // prints "ZZ"
    Console.WriteLine(bijBase26.ToBijective(16384)); // prints "XFD"
}

Максимальный индекс столбца Excel 16384/ XFD, но этот код преобразует любое положительное число.

В качестве дополнительного бонуса теперь мы можем легко конвертировать в любую биективную базу. Например, для биективной базы-10 :

      static void Main(string[] args)
{
    BijectiveNumeration bijBase10 = new BijectiveNumeration(
        10,
        (value) => value < 10 ? Convert.ToChar('0'+value) : 'A'
    );

    Console.WriteLine(bijBase10.ToBijective(1));     // prints "1"
    Console.WriteLine(bijBase10.ToBijective(10));    // prints "A"
    Console.WriteLine(bijBase10.ToBijective(123));   // prints "123"
    Console.WriteLine(bijBase10.ToBijective(20));    // prints "1A"
    Console.WriteLine(bijBase10.ToBijective(100));   // prints "9A"
    Console.WriteLine(bijBase10.ToBijective(101));   // prints "A1"
    Console.WriteLine(bijBase10.ToBijective(2010));  // prints "19AA"
}

1 Этот общий ответ в конечном итоге может быть сведен к другим, правильным, конкретным ответам, но мне трудно полностью понять логику решений без формальной теории биективной нумерации в целом. Это также прекрасно доказывает его правильность. Кроме того, несколько похожих вопросов связаны с этим, некоторые из них не зависят от языка или являются более общими. Вот почему я подумал, что добавление этого ответа оправдано, и что этот вопрос был хорошим местом для его постановки.

2 Заявление об отказе от ответственности за C#: я реализовал пример на C#, потому что это то, о чем здесь спрашивают, но я никогда не изучал и не использовал этот язык. Я убедился, что он компилируется и запускается, но при необходимости адаптируйте его в соответствии с лучшими языковыми практиками / общими соглашениями.

3 Этот пример предназначен только для того, чтобы быть правильным и понятным; его можно и нужно оптимизировать, если это будет иметь значение для производительности (например, с помощью хвостовой рекурсии, но это, похоже, требует трамплина в C#) и сделать его более безопасным (например, путем проверки параметров).

int nCol = 127;
string sChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string sCol = "";
while (nCol >= 26)
{
    int nChar = nCol % 26;
    nCol = (nCol - nChar) / 26;
    // You could do some trick with using nChar as offset from 'A', but I am lazy to do it right now.
    sCol = sChars[nChar] + sCol;
}
sCol = sChars[nCol] + sCol;

Обновление: комментарий Peter прав. Вот что я получаю за написание кода в браузере.:-) Мое решение не компилировалось, оно пропускало крайнюю левую букву и строило строку в обратном порядке - все теперь исправлено.

Помимо ошибок, алгоритм в основном конвертирует число из базы 10 в базу 26.

Обновление 2: Joel Coehoorn прав: приведенный выше код вернет AB для 27. Если бы это было действительное число с основанием 26, AA было бы равно A, а следующее число после Z было бы BA.

int nCol = 127;
string sChars = "0ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string sCol = "";
while (nCol > 26)
{
    int nChar = nCol % 26;
    if (nChar == 0)
        nChar = 26;
    nCol = (nCol - nChar) / 26;
    sCol = sChars[nChar] + sCol;
}
if (nCol != 0)
    sCol = sChars[nCol] + sCol;

Я удивлен, что все решения до сих пор содержат итерацию или рекурсию.

Вот мое решение, которое работает в постоянное время (без петель). Это решение работает для всех возможных столбцов Excel и проверяет возможность преобразования входных данных в столбец Excel. Возможные столбцы находятся в диапазоне [A, XFD] или [1, 16384]. (Это зависит от вашей версии Excel)

private static string Turn(uint col)
{
    if (col < 1 || col > 16384) //Excel columns are one-based (one = 'A')
        throw new ArgumentException("col must be >= 1 and <= 16384");

    if (col <= 26) //one character
        return ((char)(col + 'A' - 1)).ToString();

    else if (col <= 702) //two characters
    {
        char firstChar = (char)((int)((col - 1) / 26) + 'A' - 1);
        char secondChar = (char)(col % 26 + 'A' - 1);

        if (secondChar == '@') //Excel is one-based, but modulo operations are zero-based
            secondChar = 'Z'; //convert one-based to zero-based

        return string.Format("{0}{1}", firstChar, secondChar);
    }

    else //three characters
    {
        char firstChar = (char)((int)((col - 1) / 702) + 'A' - 1);
        char secondChar = (char)((col - 1) / 26 % 26 + 'A' - 1);
        char thirdChar = (char)(col % 26 + 'A' - 1);

        if (thirdChar == '@') //Excel is one-based, but modulo operations are zero-based
            thirdChar = 'Z'; //convert one-based to zero-based

        return string.Format("{0}{1}{2}", firstChar, secondChar, thirdChar);
    }
}

..И конвертируется в php:

function GetExcelColumnName($columnNumber) {
    $columnName = '';
    while ($columnNumber > 0) {
        $modulo = ($columnNumber - 1) % 26;
        $columnName = chr(65 + $modulo) . $columnName;
        $columnNumber = (int)(($columnNumber - $modulo) / 26);
    }
    return $columnName;
}

Та же реализация в Java

public String getExcelColumnName (int columnNumber) 
    {     
        int dividend = columnNumber;   
        int i;
        String columnName = "";     
        int modulo;     
        while (dividend > 0)     
        {        
            modulo = (dividend - 1) % 26;         
            i = 65 + modulo;
            columnName = new Character((char)i).toString() + columnName;        
            dividend = (int)((dividend - modulo) / 26);    
        }       
        return columnName; 
    }  

Легко с рекурсией.

public static string GetStandardExcelColumnName(int columnNumberOneBased)
{
  int baseValue = Convert.ToInt32('A');
  int columnNumberZeroBased = columnNumberOneBased - 1;

  string ret = "";

  if (columnNumberOneBased > 26)
  {
    ret = GetStandardExcelColumnName(columnNumberZeroBased / 26) ;
  }

  return ret + Convert.ToChar(baseValue + (columnNumberZeroBased % 26) );
}

Просто добавьте простую двухстрочную реализацию C# с использованием рекурсии, потому что все ответы здесь кажутся гораздо более сложными, чем необходимо.

/// <summary>
/// Gets the column letter(s) corresponding to the given column number.
/// </summary>
/// <param name="column">The one-based column index. Must be greater than zero.</param>
/// <returns>The desired column letter, or an empty string if the column number was invalid.</returns>
public static string GetColumnLetter(int column) {
    if (column < 1) return String.Empty;
    return GetColumnLetter((column - 1) / 26) + (char)('A' + (column - 1) % 26);
}

Я хотел добавить свой статический класс, который я использую, для взаимодействия между индексом col и col Label. Я использую измененный принятый ответ для моего метода ColumnLabel

public static class Extensions
{
    public static string ColumnLabel(this int col)
    {
        var dividend = col;
        var columnLabel = string.Empty;
        int modulo;

        while (dividend > 0)
        {
            modulo = (dividend - 1) % 26;
            columnLabel = Convert.ToChar(65 + modulo).ToString() + columnLabel;
            dividend = (int)((dividend - modulo) / 26);
        } 

        return columnLabel;
    }
    public static int ColumnIndex(this string colLabel)
    {
        // "AD" (1 * 26^1) + (4 * 26^0) ...
        var colIndex = 0;
        for(int ind = 0, pow = colLabel.Count()-1; ind < colLabel.Count(); ++ind, --pow)
        {
            var cVal = Convert.ToInt32(colLabel[ind]) - 64; //col A is index 1
            colIndex += cVal * ((int)Math.Pow(26, pow));
        }
        return colIndex;
    }
}

Используйте это как...

30.ColumnLabel(); // "AD"
"AD".ColumnIndex(); // 30

Немного опоздал к игре, но вот код, который я использую (в C#):

private static readonly string _Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static int ColumnNameParse(string value)
{
    // assumes value.Length is [1,3]
    // assumes value is uppercase
    var digits = value.PadLeft(3).Select(x => _Alphabet.IndexOf(x));
    return digits.Aggregate(0, (current, index) => (current * 26) + (index + 1));
}

Посмотрев все предоставленные здесь версии, я решил сделать одну сам, используя рекурсию.

Вот моя версия vb.net:

Function CL(ByVal x As Integer) As String
    If x >= 1 And x <= 26 Then
        CL = Chr(x + 64)
    Else
        CL = CL((x - x Mod 26) / 26) & Chr((x Mod 26) + 1 + 64)
    End If
End Function

Если вы просто хотите это для формулы ячейки без кода, вот формула для нее:

IF(COLUMN()>=26,CHAR(ROUND(COLUMN()/26,1)+64)&CHAR(MOD(COLUMN(),26)+64),CHAR(COLUMN()+64))
private String getColumn(int c) {
    String s = "";
    do {
        s = (char)('A' + (c % 26)) + s;
        c /= 26;
    } while (c-- > 0);
    return s;
}

Это не совсем база 26, в системе нет 0. Если бы это было, за "Z" следовал бы "BA", а не "AA".

В Дельфи (Паскаль):

function GetExcelColumnName(columnNumber: integer): string;
var
  dividend, modulo: integer;
begin
  Result := '';
  dividend := columnNumber;
  while dividend > 0 do begin
    modulo := (dividend - 1) mod 26;
    Result := Chr(65 + modulo) + Result;
    dividend := (dividend - modulo) div 26;
  end;
end;

В perl для ввода 1 (A), 27 (AA) и т. Д.

sub excel_colname {
  my ($idx) = @_;       # one-based column number
  --$idx;               # zero-based column index
  my $name = "";
  while ($idx >= 0) {
    $name .= chr(ord("A") + ($idx % 26));
    $idx   = int($idx / 26) - 1;
  }
  return scalar reverse $name;
}

Уже более 30 решений, но вот мое однострочное решение C#...

public string IntToExcelColumn(int i)
{
    return ((i<16926? "" : ((char)((((i/26)-1)%26)+65)).ToString()) + (i<2730? "" : ((char)((((i/26)-1)%26)+65)).ToString()) + (i<26? "" : ((char)((((i/26)-1)%26)+65)).ToString()) + ((char)((i%26)+65)));
}

Хотя я опаздываю к игре, ответ Грэма далеко не оптимален. В частности, вам не нужно использовать modulo, вызов ToString() и применить (int) бросать. Учитывая, что в большинстве случаев в мире C# вы начинаете нумерацию с 0, вот моя ревизия:

public static string GetColumnName(int index) // zero-based
{
    const byte BASE = 'Z' - 'A' + 1;
    string name = String.Empty;

    do
    {
        name = Convert.ToChar('A' + index % BASE) + name;
        index = index / BASE - 1;
    }
    while (index >= 0);

    return name;
}

Для чего это стоит, вот код Грэма в Powershell:

function ConvertTo-ExcelColumnID {
param (
    [parameter(Position = 0,
        HelpMessage = "A 1-based index to convert to an excel column ID. e.g. 2 => 'B', 29 => 'AC'",
        Mandatory = $true)]
    [int]$index
);

[string]$result = '';
if ($index -le 0 ) {
    return $result;
}

while ($index -gt 0) {
    [int]$modulo = ($index - 1) % 26;
    $character = [char]($modulo + [int][char]'A');
    $result = $character + $result;
    [int]$index = ($index - $modulo) / 26;
}

return $result;

}

Это вопрос, который перенаправляют все остальные, а также Google, поэтому я публикую это здесь.

Многие из этих ответов верны, но слишком громоздки для простых ситуаций, например, когда у вас нет более 26 столбцов. Если у вас есть какие-либо сомнения в том, что вы можете перейти в двухбуквенные столбцы, тогда проигнорируйте этот ответ, но если вы уверены, что не будете, то вы можете сделать это так же просто, как это в C#:

public static char ColIndexToLetter(short index)
{
    if (index < 0 || index > 25) throw new ArgumentException("Index must be between 0 and 25.");
    return (char)('A' + index);
}

Черт возьми, если вы уверены в том, что вы передаете, вы можете даже удалить проверку и использовать эту строку:

(char)('A' + index)

Это будет очень похоже на многие языки, так что вы можете адаптировать его по мере необходимости.

Опять же, используйте это, только если вы на 100% уверены, что у вас не будет более 26 столбцов.

Извините, это Python вместо C#, но по крайней мере результаты верны:

def excel_column_number_to_name(column_number):
    output = ""
    index = column_number-1
    while index >= 0:
        character = chr((index%26)+ord('A'))
        output = output + character
        index = index/26 - 1

    return output[::-1]


for i in xrange(1, 1024):
    print "%4d : %s" % (i, excel_column_number_to_name(i))

Пройдены эти тесты:

  • Номер столбца: 494286 => ABCDZ
  • Номер столбца: 27 => AA
  • Номер столбца: 52 => AZ

Вот версия Actionscript:

private var columnNumbers:Array = ['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'];

    private function getExcelColumnName(columnNumber:int) : String{
        var dividend:int = columnNumber;
        var columnName:String = "";
        var modulo:int;

        while (dividend > 0)
        {
            modulo = (dividend - 1) % 26;
            columnName = columnNumbers[modulo] + columnName;
            dividend = int((dividend - modulo) / 26);
        } 

        return columnName;
    }

Еще один способ VBA

Public Function GetColumnName(TargetCell As Range) As String
    GetColumnName = Split(CStr(TargetCell.Address), "$")(1)
End Function

Решение JavaScript

/**
 * Calculate the column letter abbreviation from a 1 based index
 * @param {Number} value
 * @returns {string}
 */
getColumnFromIndex = function (value) {
    var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
    var remainder, result = "";
    do {
        remainder = value % 26;
        result = base[(remainder || 26) - 1] + result;
        value = Math.floor(value / 26);
    } while (value > 0);
    return result;
};

Уточнение исходного решения (в C#):

public static class ExcelHelper
{
    private static Dictionary<UInt16, String> l_DictionaryOfColumns;

    public static ExcelHelper() {
        l_DictionaryOfColumns = new Dictionary<ushort, string>(256);
    }

    public static String GetExcelColumnName(UInt16 l_Column)
    {
        UInt16 l_ColumnCopy = l_Column;
        String l_Chars = "0ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        String l_rVal = "";
        UInt16 l_Char;


        if (l_DictionaryOfColumns.ContainsKey(l_Column) == true)
        {
            l_rVal = l_DictionaryOfColumns[l_Column];
        }
        else
        {
            while (l_ColumnCopy > 26)
            {
                l_Char = l_ColumnCopy % 26;
                if (l_Char == 0)
                    l_Char = 26;

                l_ColumnCopy = (l_ColumnCopy - l_Char) / 26;
                l_rVal = l_Chars[l_Char] + l_rVal;
            }
            if (l_ColumnCopy != 0)
                l_rVal = l_Chars[l_ColumnCopy] + l_rVal;

            l_DictionaryOfColumns.ContainsKey(l_Column) = l_rVal;
        }

        return l_rVal;
    }
}

Эти мои коды для преобразования определенного числа (индекс начинается с 1) в столбце Excel.

    public static string NumberToExcelColumn(uint number)
    {
        uint originalNumber = number;

        uint numChars = 1;
        while (Math.Pow(26, numChars) < number)
        {
            numChars++;

            if (Math.Pow(26, numChars) + 26 >= number)
            {
                break;
            }               
        }

        string toRet = "";
        uint lastValue = 0;

        do
        {
            number -= lastValue;

            double powerVal = Math.Pow(26, numChars - 1);
            byte thisCharIdx = (byte)Math.Truncate((columnNumber - 1) / powerVal);
            lastValue = (int)powerVal * thisCharIdx;

            if (numChars - 2 >= 0)
            {
                double powerVal_next = Math.Pow(26, numChars - 2);
                byte thisCharIdx_next = (byte)Math.Truncate((columnNumber - lastValue - 1) / powerVal_next);
                int lastValue_next = (int)Math.Pow(26, numChars - 2) * thisCharIdx_next;

                if (thisCharIdx_next == 0 && lastValue_next == 0 && powerVal_next == 26)
                {
                    thisCharIdx--;
                    lastValue = (int)powerVal * thisCharIdx;
                }
            }

            toRet += (char)((byte)'A' + thisCharIdx + ((numChars > 1) ? -1 : 0));

            numChars--;
        } while (numChars > 0);

        return toRet;
    }

Мой юнит тест:

    [TestMethod]
    public void Test()
    {
        Assert.AreEqual("A", NumberToExcelColumn(1));
        Assert.AreEqual("Z", NumberToExcelColumn(26));
        Assert.AreEqual("AA", NumberToExcelColumn(27));
        Assert.AreEqual("AO", NumberToExcelColumn(41));
        Assert.AreEqual("AZ", NumberToExcelColumn(52));
        Assert.AreEqual("BA", NumberToExcelColumn(53));
        Assert.AreEqual("ZZ", NumberToExcelColumn(702));
        Assert.AreEqual("AAA", NumberToExcelColumn(703));
        Assert.AreEqual("ABC", NumberToExcelColumn(731));
        Assert.AreEqual("ACQ", NumberToExcelColumn(771));
        Assert.AreEqual("AYZ", NumberToExcelColumn(1352));
        Assert.AreEqual("AZA", NumberToExcelColumn(1353));
        Assert.AreEqual("AZB", NumberToExcelColumn(1354));
        Assert.AreEqual("BAA", NumberToExcelColumn(1379));
        Assert.AreEqual("CNU", NumberToExcelColumn(2413));
        Assert.AreEqual("GCM", NumberToExcelColumn(4823));
        Assert.AreEqual("MSR", NumberToExcelColumn(9300));
        Assert.AreEqual("OMB", NumberToExcelColumn(10480));
        Assert.AreEqual("ULV", NumberToExcelColumn(14530));
        Assert.AreEqual("XFD", NumberToExcelColumn(16384));
    }
Другие вопросы по тегам