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

475

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

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

robertkroll
источник
1
Не забывая, что существуют ограничения на количество доступных столбцов. Например, * Excel 2003 (v11) имеет размер IV, 2 ^ 8 или 256 столбцов). * Excel 2007 (v12) имеет размер XFD, 2 ^ 14 или 16384 столбца.
нарезки
Этот вопрос помечен C # и Excel. Я отмечаю этот вопрос как устаревший, потому что мы живем в 2016 году и есть EPPLUS . Обычно используемая библиотека C # для создания расширенных электронных таблиц Excel на сервере. Который предоставляется по лицензии GNU Library General Public License (LGPL). Используя EPPlus, вы можете легко получить строку столбца.
Tony_KiloPapaMikeGolf
Обратите внимание, что пределы строк и столбцов зависят больше от формата файла, чем версия Excel, и могут быть разными для каждой книги. Они могут измениться даже для одной и той же книги, если она сохранена в более старом или новом формате.
Слай

Ответы:

902

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

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;
}
Грэхем
источник
65
Этот код работает хорошо. Предполагается, что столбец A - это columnNumber 1. Мне пришлось быстро изменить учетную запись моей системы, используя Column A в качестве columnNumber 0. Я изменил строку int divndnd на intdivnd = columnNumber + 1; Кит
Кит Сирмонс
14
@Jduv только что попробовал, используя StringBuilder. Это занимает примерно вдвое больше времени. Это очень короткая строка (не более 3 символов - Excel 2010 переходит в столбец XFD), поэтому максимум 2 конкатенации строк. (Я использовал 100 итераций перевода целых чисел от 1 до 16384, то есть столбцы Excel от A до XFD, в качестве теста).
Грэм
18
Для лучшего понимания я бы заменил 65 на «А»
Стеф Хейенрат
11
Я думаю, что было бы лучше использовать «A» вместо 65. И 26 можно оценить как («Z» - «A» + 1), например: const int AlphabetLength = 'Z' - 'A' + 1;
Денис Гладкий
5
Хотя я опаздываю к игре, код далеко не оптимален. В частности, вам не нужно использовать modulo, вызывать ToString()и применять (int)приведение. Учитывая, что в большинстве случаев в мире C # вы начинаете нумерацию с 0, вот моя ревизия: <! - language: c # -> открытая статическая строка GetColumnName (int index) // на основе нуля {const byte BASE = 'Z '-' A '+ 1; имя строки = String.Empty; do {name = Convert.ToChar ('A' + index% BASE) + name; index = index / BASE - 1; } while (index> = 0); вернуть имя; }
Герман Кан
63

Если кому-то нужно сделать это в 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
оборота вщч
источник
2
Пример: = ПОДСТАВИТЬ (ТЕКСТ (АДРЕС (1,1000,4), ""), "1", "")
Дольф
Да, я использую Excel в локали, где; используется вместо, для разделения аргументов функции в Excel. Спасибо за указание на это.
vzczc
2
Я сделал это без функции TEXT. Какова цель функции ТЕКСТ?
Dayuloli
2
@dayuloli Давно, с тех пор как этот ответ был написан, вы правы, функция TEXT здесь не служит цели. Обновлю ответ.
vzczc
21

Извините, это 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))
Рома
источник
Да, я проголосовал, не публиковать Python, когда вопрос C #. И да, больше людей должны это делать.
KyloRen
1
Название вопроса не подразумевает C #, поэтому сюда может прийти так много людей, которые не ожидают C #. Следовательно, спасибо за участие в Python!
Тим Эрвин
20

Вам может потребоваться преобразование в обоих направлениях, например, из адреса столбца 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;
}
Arent Arntzen
источник
13

Я обнаружил ошибку в своем первом посте, поэтому я решил сесть и посчитать. Я обнаружил, что система счисления, используемая для идентификации столбцов 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;
    }
Джон
источник
о, я не знаю почему, но мне нравится это решение. Ничего особенного, просто хорошее использование логики ... легко читаемый код для уровней программиста. Однако я считаю, что лучше всего указывать пустую строку в C # как string range = string.Empty;
анонимный тип
Да, очень хорошее объяснение. Но не могли бы вы также заявить, что это не база 27? Ваше объяснение показывает это при изучении, но быстрое упоминание вверху может спасти несколько других людей.
Мариус
10
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;

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

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

Обновление 2 : Джоэл Коухорн прав: приведенный выше код вернет 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;
Франци Пенов
источник
Код не скомпилируется (sCol не инициализирован). Если это произойдет, это не даст правильный ответ.
Питер
5
ЭТО ОТВЕТ НЕПРАВИЛЬНЫЙ. Base26 не достаточно хорош. Подумайте о том, что происходит, когда вы переходите от Z к AA. Если A эквивалентно цифре 0, то это как перенос от 9 до 00. Если это 1 цифра, это как перенос от 9 до 11.
Джоэл Коухорн
4
Мне не понятно после обновлений ... один из алгоритмов теперь правильный? И если да, то какой, второй? Я отредактировал бы это и сделал бы это очевидным для потомков ....
JoeCool
10

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

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) );
}
Питер
источник
1
.. или петля. Нет реальной причины использовать рекурсию здесь.
Blorgbeard выходит
1
Это не просто база 26, поэтому рекурсивное решение намного проще.
Джоэл Кохорн
Эта рутина на самом деле не работает. Например, GetStandardExcelColumnName (26) возвращает @ GetStandardExcelColumnName (52) возвращает B @
sgmoore
1
самое ясное, а не самое простое решение - лучшее.
анонимный тип
9

Просто добавьте простую двухстрочную реализацию 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);
}
оборота Экстрагорей
источник
8

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

function GetExcelColumnName($columnNumber) {
    $columnName = '';
    while ($columnNumber > 0) {
        $modulo = ($columnNumber - 1) % 26;
        $columnName = chr(65 + $modulo) . $columnName;
        $columnNumber = (int)(($columnNumber - $modulo) / 26);
    }
    return $columnName;
}
Стивен Фури
источник
Используйте ord('A')вместо 65.
Mulli
8

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

Вот мое решение, которое работает в постоянное время (без петель). Это решение работает для всех возможных столбцов 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);
    }
}
user2023861
источник
2
К вашему сведению: @ ответ Грэма (и, возможно, других) носит более общий характер, чем ваш: они поддерживают 4+ символов в именах столбцов. И именно поэтому они итеративны.
Бернард Полус
Фактически, если бы они использовали неограниченные целые числа, а не ints, результирующее имя столбца могло быть произвольно длинным (например, в случае с ответом python)
Бернард Полус
1
Если мои данные будут слишком большими для таблицы из 16384 столбцов, я выстрелю себе в голову. В любом случае, Excel даже не поддерживает все возможные трехбуквенные столбцы (он обрезается в XFD, оставляя 1894 столбца). Прямо сейчас в любом случае. Я буду обновлять свой ответ в будущем по мере необходимости.
user2023861
:) не знал этого! Мой комментарий был о теоретических свойствах различных алгоритмов.
Бернард Полус
Это, вероятно, самое простое и ясное решение.
Хуан
8

Этот ответ в 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;
}
MaVRoSCy
источник
6

Та же реализация в 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; 
    }  
Balaji.NS
источник
5

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

Вот моя версия 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
user932708
источник
5

Немного опоздал к игре, но вот код, который я использую (в 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));
}
Келли Л
источник
2
Вы сделали обратное тому, что спросили, но +1 для вашего лямбда-фу.
Нуреттин
IndexOfдовольно медленно, вам лучше пересчитать обратное отображение.
Влад
5

Я хотел добавить свой статический класс, который я использую, для взаимодействия между индексом 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
t3dodson
источник
4

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

IF(COLUMN()>=26,CHAR(ROUND(COLUMN()/26,1)+64)&CHAR(MOD(COLUMN(),26)+64),CHAR(COLUMN()+64))
Мэтт Льюис
источник
4

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

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;
JRL
источник
3
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».

rubberduckie
источник
3

В 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;
}
Myforwik
источник
2

Уточнение исходного решения (в 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;
    }
}
ShloEmi
источник
2

Вот версия 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;
    }
обкрадывать
источник
2

Решение 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;
};
союзник
источник
1
Попробуйте индексы 26 и 27. Это очень близко, но выключено на один.
Эрик
значение = Math.floor (значение / 26); должно быть значение = Math.ceil (значение / 26) - 1;
Генри Лю
2

Эти мои коды для преобразования определенного числа (индекс начинается с 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));
    }
оборота Стеванус
источник
+1 за показание максимальной ссылки на ячейку в ваших тестах (XFD) - вы не поверите, насколько трудно найти эту информацию в Интернете.
блок управления
2

Хотя я опаздываю к игре, ответ Грэма далеко не оптимален. В частности, вам не нужно использовать 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;
}
Герман Кан
источник
2

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

Public Function GetColumnName(TargetCell As Range) As String
    GetColumnName = Split(CStr(TargetCell.Cells(1, 1).Address), "$")(1)
End Function
Paul Ma
источник
1

Вот моя супер поздняя реализация в PHP. Этот рекурсивный. Я написал это как раз перед тем, как нашел этот пост. Я хотел посмотреть, решили ли другие эту проблему уже ...

public function GetColumn($intNumber, $strCol = null) {

    if ($intNumber > 0) {
        $intRem = ($intNumber - 1) % 26;
        $strCol = $this->GetColumn(intval(($intNumber - $intRem) / 26), sprintf('%s%s', chr(65 + $intRem), $strCol));
    }

    return $strCol;
}
Ян Аткин
источник
1

Я пытаюсь сделать то же самое в Java ... Я написал следующий код:

private String getExcelColumnName(int columnNumber) {

    int dividend = columnNumber;
    String columnName = "";
    int modulo;

    while (dividend > 0)
    {
        modulo = (dividend - 1) % 26;

        char val = Character.valueOf((char)(65 + modulo));

        columnName += val;

        dividend = (int)((dividend - modulo) / 26);
    } 

    return columnName;
}

Теперь, когда я запустил его с columnNumber = 29, он дает мне результат = "CA" (вместо "AC") любые комментарии, которые я пропускаю? Я знаю, что могу изменить это с помощью StringBuilder .... Но, глядя на ответ Грэма, я немного растерялся ...

Хасан
источник
Грэм говорит: columnName = Convert.ToChar(65 + modulo).ToString() + columnName(то есть значение + ColName). Хасан говорит: columnName += val;(то есть ColName + значение)
mcalex
Вы добавляете нового персонажа вместо того, чтобы добавлять его. Должно быть columnName = columnName + val.
Доменик Д.
1

Изысканная и элегантная версия Ruby :

def col_name(col_idx)
    name = ""
    while col_idx>0
        mod     = (col_idx-1)%26
        name    = (65+mod).chr + name
        col_idx = ((col_idx-mod)/26).to_i
    end
    name
end
Иван Б.
источник
1

Это вопрос, который перенаправляют все остальные, а также 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 столбцов .

Влад Шнаковски
источник
1

Спасибо за ответы здесь! помог мне придумать эти вспомогательные функции для некоторого взаимодействия с API Google Sheets, над которым я работаю в Elixir / Phoenix

вот что я придумал (возможно, мог бы использовать дополнительную проверку и обработку ошибок)

В эликсире:

def number_to_column(number) do
  cond do
    (number > 0 && number <= 26) ->
      to_string([(number + 64)])
    (number > 26) ->
      div_col = number_to_column(div(number - 1, 26))
      remainder = rem(number, 26)
      rem_col = cond do
        (remainder == 0) ->
          number_to_column(26)
        true ->
          number_to_column(remainder)
      end
      div_col <> rem_col
    true ->
      ""
  end
end

И обратная функция:

def column_to_number(column) do
  column
    |> to_charlist
    |> Enum.reverse
    |> Enum.with_index
    |> Enum.reduce(0, fn({char, idx}, acc) ->
      ((char - 64) * :math.pow(26,idx)) + acc
    end)
    |> round
end

И некоторые тесты:

describe "test excel functions" do
  @excelTestData [{"A", 1}, {"Z",26}, {"AA", 27}, {"AB", 28}, {"AZ", 52},{"BA", 53}, {"AAA", 703}]

  test "column to number" do
    Enum.each(@excelTestData, fn({input, expected_result}) ->
      actual_result = BulkOnboardingController.column_to_number(input)
      assert actual_result == expected_result
    end)
  end

  test "number to column" do
    Enum.each(@excelTestData, fn({expected_result, input}) ->
      actual_result = BulkOnboardingController.number_to_column(input)
      assert actual_result == expected_result
    end)
  end
end
оборота
источник