Como converter um número de coluna (por exemplo, 127) em uma coluna do Excel (por exemplo, AA)


474

Como você converte um número numérico em um nome de coluna do Excel em C # sem usar a automação para obter o valor diretamente do Excel.

O Excel 2007 tem um intervalo possível de 1 a 16384, que é o número de colunas que ele oferece suporte. Os valores resultantes devem estar na forma de nomes de colunas do Excel, por exemplo, A, AA, AAA etc.


1
Não esquecendo que existem limites no número de colunas disponíveis. Por exemplo, * Excel 2003 (v11) sobe para IV, 2 ^ 8 ou 256 colunas). * O Excel 2007 (v12) vai para colunas XFD, 2 ^ 14 ou 16384.
unsliced


Esta questão está etiquetada com C # e excel. Eu sinalizo esta pergunta como desatualizada, porque vivemos em 2016 e existe o EPPLUS . Uma biblioteca C # comumente usada para criar planilhas avançadas do Excel no servidor. Disponível em: GNU Library General Public License (LGPL). Usando o EPPlus, você pode facilmente obter a string da coluna.
Tony_KiloPapaMikeGolf

Observe que os limites de linha e coluna dependem mais do formato do arquivo que da versão do Excel e podem ser diferentes para cada pasta de trabalho. Eles podem ser alterados mesmo para a mesma pasta de trabalho se ela for salva em um formato mais antigo ou mais recente.
Slai

Respostas:


901

Aqui está como eu faço:

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

64
Esse código funciona bem. Ele assume que a Coluna A é columnNumber 1. Eu tive que fazer uma alteração rápida para contabilizar meu sistema usando a Coluna A como columnNumber 0. Alterei a linha int dividend para int dividend = columnNumber + 1; Keith
Keith Sirmons

14
@Jduv acabou de experimentar usando StringBuilder. Demora cerca do dobro do tempo. É uma sequência muito curta (no máximo 3 caracteres - o Excel 2010 vai até a coluna XFD), portanto, no máximo 2 concatenações de sequência. (Eu usei 100 iterações para traduzir os números inteiros 1 a 16384, ou seja, as colunas Excel A a XFD, como teste).
Graham

18
Para uma melhor compreensão, eu substituiria o 65 por 'A'
Stef Heyenrath 11/11

11
Eu acho que seria melhor usar 'A' em vez de 65. E 26 poderiam ser avaliados como ('Z' - 'A' + 1), por exemplo: const int AlphabetLength = 'Z' - 'A' + 1;
Denis Gladkiy #

5
Embora eu esteja atrasado para o jogo, o código está longe de ser ideal. Particularmente, você não precisa usar modulo, chamar ToString()e aplicar o (int)elenco. Considerando que na maioria dos casos no mundo C # você começaria a numerar de 0, aqui está minha revisão: <! - language: c # -> string estática pública GetColumnName (int index) // com base em zero {const byte BASE = 'Z '-' A '+ 1; nome da string = String.Empty; do {name = Convert.ToChar ('A' + índice% BASE) + nome; índice = índice / BASE - 1; } while (índice> = 0); nome de retorno; }
Herman Kan

63

Se alguém precisar fazer isso no Excel sem o VBA, aqui está uma maneira:

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

onde colNum é o número da coluna

E no 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
Exemplo: = SUBSTITUTO (TEXTO (ENDEREÇO ​​(1,1000,4), ""), "1", "")
Dolph

Sim, eu uso o Excel em um local onde; é usado no lugar de, para separar argumentos de função no Excel. Obrigado por apontar isso.
vzczc

2
Eu fiz isso sem a função TEXT. Qual é o objetivo da função TEXT?
dayuloli

2
@dayuloli Muito tempo desde que esta resposta foi escrita, você está correto, a função TEXT não serve a um propósito aqui. Atualizará a resposta.
vzczc

21

Desculpe, este é Python em vez de C #, mas pelo menos os resultados estão corretos:

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

Sim, eu votei, não poste Python quando a pergunta for c #. E sim, mais pessoas devem fazer isso.
KyloRen

1
O título da pergunta não implica C #, muitas pessoas podem vir aqui que não esperam C #. Por isso, obrigado por compartilhar em Python!
Tim-Erwin

20

Você pode precisar de conversão nos dois sentidos, por exemplo, do endereço da coluna do Excel como AAZ para número inteiro e de qualquer número inteiro para o Excel. Os dois métodos abaixo farão exatamente isso. Assume uma indexação baseada em 1, o primeiro elemento em suas "matrizes" é o número 1 do elemento. Aqui não há limites de tamanho, então você pode usar endereços como ERROR e esse seria o número da coluna 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;
}

13

Descobri um erro no meu primeiro post, então decidi me sentar e fazer as contas. O que descobri é que o sistema numérico usado para identificar as colunas do Excel não é um sistema básico 26, como outra pessoa postou. Considere o seguinte na base 10. Você também pode fazer isso com as letras do alfabeto.

Espaço: ......................... S1, S2, S3: S1, S2, S3
............ ....................... 0, 00, 000: .. A, AA, AAA
............. ....................... 1, 01, 001: .. B, AB, AAB
.............. ......................…,…,…: ..…,…,…
............... ..................... 9, 99, 999: .. Z, ZZ, ZZZ
Total de estados no espaço: 10, 100, 1000: 26, 676, 17576
Total de estados: ............... 1110 ................ 18278

O Excel numera colunas nos espaços alfabéticos individuais usando a base 26. Você pode ver que, em geral, a progressão do espaço de estados é a, a ^ 2, a ^ 3,… para alguma base a, e o número total de estados é a + a ^ 2 + a ^ 3 +….

Suponha que você queira encontrar o número total de estados A nos primeiros N espaços. A fórmula para fazer isso é A = (a) (a ^ N - 1) / (a-1). Isso é importante porque precisamos encontrar o espaço N que corresponde ao nosso índice K. Se eu quiser descobrir onde K se encontra no sistema numérico, preciso substituir A por K e resolver por N. A solução é N = log { base a} (A (a-1) / a +1). Se eu usar o exemplo de a = 10 e K = 192, sei que N = 2,23804…. Isso me diz que K está no início do terceiro espaço, pois é um pouco maior que dois.

O próximo passo é descobrir exatamente a que distância estamos do espaço atual. Para encontrar isso, subtraia de K o A gerado usando o piso de N. Neste exemplo, o piso de N é dois. Portanto, A = (10) (10 ^ 2 - 1) / (10-1) = 110, conforme o esperado quando você combina os estados dos dois primeiros espaços. Isso precisa ser subtraído de K, porque esses primeiros 110 estados já teriam sido contabilizados nos dois primeiros espaços. Isso nos deixa com 82 estados. Portanto, neste sistema numérico, a representação de 192 na base 10 é 082.

O código C # usando um índice base zero é

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

// Postagem antiga

Uma solução baseada em zero em 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;
    }

ooh eu não sei porque, mas eu gosto desta solução. Nada gosta apenas de um bom uso da lógica ... código facilmente legível para níveis de programador. Uma coisa, porém, acredito que é melhor prática especificar uma string vazia em C # como string range = string.Empty;
Tipo anônimo

Sim, explicação muito boa. Mas você também pode afirmar que também não é a base 27? Sua explicação mostra isso quando estudado, mas uma rápida menção no topo pode economizar algumas outras pessoas algum tempo.
Marius

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;

Atualização : O comentário de Peter está certo. É o que recebo para escrever código no navegador. :-) Minha solução não estava compilando, estava faltando a letra mais à esquerda e estava construindo a sequência na ordem inversa - tudo agora corrigido.

Bugs à parte, o algoritmo está basicamente convertendo um número da base 10 para a base 26.

Atualização 2 : Joel Coehoorn está certo - o código acima retornará AB para 27. Se fosse o número base 26 real, AA seria igual a A e o próximo número depois de Z seria 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;

O código não será compilado (sCol não inicializado). Se isso acontecer, não dará a resposta certa.
Peter

5
ESTA RESPOSTA ESTÁ ERRADA. Base26 não é bom o suficiente. Pense no que acontece quando você passa de Z para AA. Se A é equivalente a 0 dígito, então é como wraping de 9 a 00. Se for o 1 dígitos, é como acondicionamento de 9 a 11.
Joel Coehoorn

4
Não estou claro após as atualizações ... agora algum dos algoritmos está correto? E se sim, qual, o segundo? Eu editar esta e torná-lo óbvio para a posteridade ....
JoeCool

10

Fácil com recursão.

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
.. ou um loop. Não há motivo real para usar a recursão aqui.
Blorgbeard sai em 08/10/08

1
Não é apenas a base 26, então a solução recursiva é muito mais simples.
Joel Coehoorn

Esta rotina não funciona realmente. Por exemplo, GetStandardExcelColumnName (26) retorna @ GetStandardExcelColumnName (52) retorna B @
sgmoore

1
a solução mais clara, em oposição à solução "mais simples", é a melhor.
Tipo anônimo

9

Apenas lançando uma implementação simples de C # de duas linhas usando recursão, porque todas as respostas aqui parecem muito mais complicadas do que o necessário.

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

..E convertido em php:

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

Use em ord('A')vez de 65.
Mulli 07/07/19

8

Estou surpreso que todas as soluções até agora contenham iteração ou recursão.

Aqui está a minha solução que é executada em tempo constante (sem loops). Esta solução funciona para todas as colunas possíveis do Excel e verifica se a entrada pode ser transformada em uma coluna do Excel. As colunas possíveis estão no intervalo [A, XFD] ou [1, 16384]. (Isso depende da sua versão do 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);
    }
}

2
FYI: @ A resposta de Graham (e provavelmente as outras) é mais geral que a sua: elas suportam 4 ou mais caracteres nos nomes das colunas. E é exatamente por isso que são iterativos.
bernard Paulus

Na verdade, se eles usaram inteiros ilimitadas e não inté, o nome da coluna resultante poderia ser arbitrariamente longo (que é o caso da resposta python, por exemplo)
bernard Paulus

1
Se meus dados forem muito grandes para uma planilha de 16.384 colunas, vou me dar um tiro na cabeça. De qualquer forma, o Excel nem mesmo suporta todas as colunas possíveis de três letras (é cortado no XFD, deixando 1.894 colunas). Agora mesmo assim. Atualizarei minha resposta no futuro, conforme necessário.
user2023861

:) não sabia disso! Meu comentário foi sobre as propriedades teóricas dos diferentes algoritmos.
22133 bernard paulus

Esta é provavelmente a solução mais simples e clara.
Juan

8

Esta resposta está em 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;
}

6

A mesma implementação em 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; 
    }  

5

Depois de examinar todas as versões fornecidas aqui, desci para fazer uma, usando recursão.

Aqui está a minha versão do 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

5

Um pouco tarde para o jogo, mas aqui está o código que eu uso (em 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
Você fez o inverso do que foi solicitado, mas marcou +1 no seu lambda-fu.
nurettin

IndexOfé bastante lento, é melhor pré-calcular o mapeamento reverso.
25412 Vlad

5

Eu queria jogar na minha classe estática que eu uso, para interoperabilidade entre o índice col e o col Label. Eu uso uma resposta aceita modificada para o meu método 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;
    }
}

Use isso como ...

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

4

se você apenas deseja uma fórmula de célula sem código, aqui está uma fórmula:

IF(COLUMN()>=26,CHAR(ROUND(COLUMN()/26,1)+64)&CHAR(MOD(COLUMN(),26)+64),CHAR(COLUMN()+64))

4

Em Delphi (Pascal):

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;

3
private String getColumn(int c) {
    String s = "";
    do {
        s = (char)('A' + (c % 26)) + s;
        c /= 26;
    } while (c-- > 0);
    return s;
}

Não é exatamente a base 26, não há 0 no sistema. Se houvesse, 'Z' seria seguido por 'BA' e não por 'AA'.


3

Em perl, para uma entrada de 1 (A), 27 (AA) etc.

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

2

Refinando a solução original (em 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;
    }
}

2

Aqui está uma versão do 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

Solução 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
Tente os índices 26 e 27. É muito próximo, mas um por um.
Eric

valor = Math.floor (valor / 26); deve ser value = Math.ceil (value / 26) - 1;
Henry Liu

2

Estes meus códigos para converter um número específico (o índice começa em 1) na coluna do 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;
    }

Meu teste de unidade:

    [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 por mostrar qual é a referência máxima de célula em seus testes (XFD) - você não acreditaria no quão difícil é encontrar essas informações na web.
controlbox

2

Embora eu esteja atrasado para o jogo, a resposta de Graham está longe de ser ótima. Particularmente, você não precisa usar o elenco modulo, ligar ToString()e aplicar o (int)elenco. Considerando que na maioria dos casos no mundo C # você começaria a numerar de 0, aqui está minha revisão:

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

Outra maneira de VBA

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

1

Aqui está minha implementação super tardia em PHP. Este é recursivo. Eu escrevi antes de encontrar este post. Eu queria ver se outras pessoas já haviam resolvido esse problema ...

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

Estou tentando fazer a mesma coisa em Java ... Eu escrevi o seguinte código:

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

Agora, quando eu o executei com columnNumber = 29, ele me dará o resultado = "CA" (em vez de "AC") algum comentário do que estou perdendo? Eu sei que posso revertê-lo por StringBuilder .... Mas, olhando para a resposta de Graham, estou um pouco confuso ...


Graham diz: columnName = Convert.ToChar(65 + modulo).ToString() + columnName(ou seja, valor + ColName). Hasan diz: columnName += val;(ie ColName + valor)
mcalex 28/03

Você está anexando o novo personagem em vez de anexá-lo. Deveria ser columnName = columnName + val.
Domenic D.

1

Versão Ruby coerente e elegante :

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

Esta é a pergunta que todos os outros, bem como o Google redirecionam, estou postando isso aqui.

Muitas dessas respostas estão corretas, mas são muito complicadas para situações simples, como quando você não tem mais de 26 colunas. Se você tiver alguma dúvida se pode entrar em colunas de caracteres duplos, ignore esta resposta, mas se tiver certeza de que não, poderá fazê-lo da maneira mais simples possível em 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);
}

Caramba, se você está confiante sobre o que está passando, pode até remover a validação e usar esta linha:

(char)('A' + index)

Isso será muito semelhante em muitos idiomas, para que você possa adaptá-lo conforme necessário.

Novamente, use isso apenas se tiver 100% de certeza de que não terá mais de 26 colunas .


1

Obrigado pelas respostas aqui !! me ajudou a criar essas funções auxiliares para alguma interação com a API do Google Sheets em que estou trabalhando no Elixir / Phoenix

aqui está o que eu vim com (provavelmente poderia usar alguma validação extra e tratamento de erros)

No Elixir:

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

E a função inversa:

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

E alguns testes:

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.