Cómo convertir un número de columna (por ejemplo, 127) en una columna de Excel (por ejemplo, AA)

475

¿Cómo convertir un número numérico a un nombre de columna de Excel en C # sin utilizar la automatización para obtener el valor directamente de Excel?

Excel 2007 tiene un rango posible de 1 a 16384, que es el número de columnas que admite. Los valores resultantes deben estar en forma de nombres de columna de Excel, por ejemplo, A, AA, AAA, etc.

robertkroll
fuente
1
Sin olvidar que hay límites en el número de columnas disponibles. Por ejemplo, * Excel 2003 (v11) sube a IV, 2 ^ 8 o 256 columnas). * Excel 2007 (v12) sube a XFD, 2 ^ 14 o 16384 columnas.
cortar
Esta pregunta está etiquetada con C # y Excel. Marco esta pregunta como obsoleta, porque vivimos en 2016 y hay EPPLUS . Una biblioteca de C # de uso común para crear hojas de cálculo avanzadas de Excel en el servidor. Que está disponible bajo: GNU Library General Public License (LGPL). Usando EPPlus puede obtener fácilmente la cadena de Columna.
Tony_KiloPapaMikeGolf
Tenga en cuenta que los límites de fila y columna dependen más del formato de archivo que la versión de Excel, y pueden ser diferentes para cada libro. Pueden cambiar incluso para el mismo libro de trabajo si se guarda en un formato más antiguo o más nuevo.
Slai

Respuestas:

902

Así es como lo hago:

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;
}
Graham
fuente
65
Este código funciona bien. Se supone que la columna A es columnNumber 1. Tuve que hacer un cambio rápido para dar cuenta de mi sistema usando la columna A como columnNumber 0. Cambié la línea int dividend a int dividend = columnNumber + 1; Keith
Keith Sirmons
14
@Jduv acaba de probarlo usando StringBuilder. Tarda aproximadamente el doble de tiempo. Es una cadena muy corta (máximo 3 caracteres: Excel 2010 sube a la columna XFD), por lo que un máximo de 2 concatenaciones de cadena. (Usé 100 iteraciones de traducción de los enteros 1 a 16384, es decir, las columnas A de Excel a XFD, como prueba).
Graham
18
Para una mejor comprensión, reemplazaría el 65 con 'A'
Stef Heyenrath
11
Creo que sería mejor usar 'A' en lugar de 65. Y 26 podría evaluarse como ('Z' - 'A' + 1), por ejemplo: const int AlphabetLength = 'Z' - 'A' + 1;
Denis Gladkiy
55
Aunque llegué tarde al juego, el código está lejos de ser óptimo. Particularmente, no tienes que usar modulo, invocar ToString()y aplicar el (int)elenco. Teniendo en cuenta que, en la mayoría de los casos, en el mundo de C # comenzaría a numerar desde 0, aquí está mi revisión: <! - idioma: c # -> cadena estática pública GetColumnName (int index) // basado en cero {const byte BASE = 'Z '-' A '+ 1; string name = String.Empty; do {name = Convert.ToChar ('A' + índice% BASE) + nombre; índice = índice / BASE - 1; } while (índice> = 0); nombre de retorno; }
Herman Kan
63

Si alguien necesita hacer esto en Excel sin VBA, aquí hay una manera:

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

donde colNum es el número de columna

Y en 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
revs vzczc
fuente
2
Ejemplo: = SUSTITUTO (TEXTO (DIRECCIÓN (1,1000,4), ""), "1", "")
Dolph
Sí, uso Excel en un entorno local donde; se usa en lugar de, para separar argumentos de funciones en Excel. Gracias por señalar esto.
vzczc
2
Hice esto sin la función TEXTO. ¿Cuál es el propósito de la función TEXTO?
dayuloli
2
@dayuloli Mucho tiempo desde que se escribió esta respuesta, estás en lo correcto, la función TEXTO no tiene un propósito aquí. Se actualizará la respuesta.
vzczc
21

Lo sentimos, este es Python en lugar de C #, pero al menos los resultados son correctos:

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))
RoMa
fuente
Sí, voté abajo, no publique Python cuando la pregunta es C #. Y sí, más personas deberían hacer esto.
KyloRen
1
El título de la pregunta no implica C #, por lo que muchas personas pueden venir aquí sin esperar C #. Por lo tanto, ¡gracias por compartir en Python!
Tim-Erwin el
20

Es posible que necesite la conversión en ambos sentidos, por ejemplo, desde la dirección de columna de Excel como AAZ a entero y de cualquier entero a Excel. Los dos métodos a continuación harán exactamente eso. Asume una indexación basada en 1, el primer elemento en sus "matrices" es el elemento número 1. No hay límites de tamaño aquí, por lo que puede usar direcciones como ERROR y ese sería el número de columna 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
fuente
13

Descubrí un error en mi primera publicación, así que decidí sentarme y hacer los cálculos. Lo que encontré es que el sistema de números utilizado para identificar columnas de Excel no es un sistema base 26, como publicó otra persona. Considere lo siguiente en la base 10. También puede hacer esto con las letras del alfabeto.

Espacio: ......................... 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 en el espacio: 10, 100, 1000: 26, 676, 17576
Total de Estados: ............... 1110 ................ 18278

Excel numera las columnas en los espacios alfabéticos individuales utilizando la base 26. Puede ver que, en general, la progresión del espacio de estados es a, a ^ 2, a ^ 3, ... para alguna base a, y el número total de estados es a + a ^ 2 + a ^ 3 +….

Suponga que desea encontrar el número total de estados A en los primeros N espacios. La fórmula para hacerlo es A = (a) (a ^ N - 1) / (a-1). Esto es importante porque necesitamos encontrar el espacio N que corresponde a nuestro índice K. Si quiero averiguar dónde se encuentra K en el sistema de números, necesito reemplazar A por K y resolver N. La solución es N = log { base a} (A (a-1) / a +1). Si uso el ejemplo de a = 10 y K = 192, sé que N = 2.23804 ... Esto me dice que K se encuentra al comienzo del tercer espacio ya que es un poco mayor que dos.

El siguiente paso es encontrar exactamente qué tan lejos estamos en el espacio actual. Para encontrar esto, reste de K el A generado usando el piso de N. En este ejemplo, el piso de N es dos. Entonces, A = (10) (10 ^ 2 - 1) / (10-1) = 110, como se espera cuando combina los estados de los dos primeros espacios. Esto debe restarse de K porque estos primeros 110 estados ya se habrían contabilizado en los dos primeros espacios. Esto nos deja con 82 estados. Entonces, en este sistema de números, la representación de 192 en la base 10 es 082.

El código C # que usa un índice base de cero es

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

// Publicación anterior

Una solución de base cero en 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;
    }
Juan
fuente
ooh, no sé por qué, pero me gusta esta solución. Nada de lujos, solo un buen uso de la lógica ... código fácilmente legible para niveles de programador. Sin embargo, creo que es una buena práctica especificar una cadena vacía en C # como string range = string.Empty;
Tipo anónimo el
Sí, muy buena explicación. Pero, ¿podría decir también que tampoco es la base 27? Su explicación muestra esto cuando se estudia, pero una mención rápida en la parte superior puede ahorrarle algo de tiempo a algunas otras personas.
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;

Actualización : el comentario de Peter es correcto. Eso es lo que obtengo por escribir código en el navegador. :-) Mi solución no estaba compilando, le faltaba la letra más a la izquierda y estaba construyendo la cadena en orden inverso, todo ahora solucionado.

Dejando a un lado los errores, el algoritmo básicamente está convirtiendo un número de la base 10 a la base 26.

Actualización 2 : Joel Coehoorn tiene razón: el código anterior devolverá AB para 27. Si fuera un número real de base 26, AA sería igual a A y el siguiente número después de Z sería 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;
revs Franci Penov
fuente
El código no se compilará (sCol no inicializado). Si lo hace, no dará la respuesta correcta.
Peter
55
ESTA RESPUESTA ES INCORRECTA. Base26 no es lo suficientemente bueno. Piensa en lo que sucede cuando envuelves de Z a AA. Si A es equivalente a 0 dígitos, entonces es como envolver de 9 a 00. Si son los 1 dígitos, es como envolver de 9 a 11.
Joel Coehoorn
44
No estoy claro después de las actualizaciones ... ¿alguno de los algoritmos es ahora correcto? Y si es así, ¿cuál, el segundo? Me edito esto y hacen evidente para la posteridad ....
JoeCool
10

Fácil con recursividad.

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) );
}
Peter
fuente
1
.. o un bucle. No hay una razón real para usar la recursividad aquí.
Blorgbeard sale el
1
No es solo la base 26, por lo que la solución recursiva es mucho más simple.
Joel Coehoorn el
Esta rutina en realidad no funciona. Por ejemplo, GetStandardExcelColumnName (26) devuelve @ GetStandardExcelColumnName (52) devuelve B @
sgmoore
1
la solución más clara en oposición a la "más simple" es la mejor.
Tipo anónimo
9

Simplemente lanzando una implementación simple de C # de dos líneas usando recursividad, porque todas las respuestas aquí parecen mucho más complicadas de lo necesario.

/// <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);
}
Extragorey
fuente
8

..Y convertido a php:

function GetExcelColumnName($columnNumber) {
    $columnName = '';
    while ($columnNumber > 0) {
        $modulo = ($columnNumber - 1) % 26;
        $columnName = chr(65 + $modulo) . $columnName;
        $columnNumber = (int)(($columnNumber - $modulo) / 26);
    }
    return $columnName;
}
Stephen Fuhry
fuente
Usar en ord('A')lugar de 65.
Mulli
8

Me sorprende que todas las soluciones hasta ahora contengan iteración o recursividad.

Aquí está mi solución que se ejecuta en tiempo constante (sin bucles). Esta solución funciona para todas las posibles columnas de Excel y verifica que la entrada se pueda convertir en una columna de Excel. Las columnas posibles están en el rango [A, XFD] o [1, 16384]. (Esto depende de su versión de 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);
    }
}
usuario2023861
fuente
2
FYI: la respuesta de @ Graham (y probablemente las otras) es más general que la suya: admiten más de 4 caracteres en los nombres de columna. Y es precisamente por eso que son iterativos.
bernard paulus
De hecho, si usaban números enteros ilimitados y no ints, el nombre de la columna resultante podría ser arbitrariamente largo (ese es el caso de la respuesta de Python, por ejemplo)
bernard paulus
1
Si mis datos son demasiado grandes para una hoja de cálculo de 16,384 columnas, me dispararé en la cabeza. De todos modos, Excel ni siquiera admite todas las posibles columnas de tres letras (se corta en XFD dejando de lado 1.894 columnas). Ahora mismo de todos modos. Actualizaré mi respuesta en el futuro según sea necesario.
user2023861
:) no sabia eso! Mi comentario fue sobre las propiedades teóricas de los diferentes algoritmos.
bernard paulus
Esta es probablemente la solución más simple y clara.
Juan
8

Esta respuesta está en 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
fuente
6

Misma implementación en 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
fuente
5

Después de mirar todas las versiones suministradas aquí, decidí hacer una yo mismo, usando la recursión.

Aquí está mi versión 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
usuario932708
fuente
5

Un poco tarde para el juego, pero aquí está el código que uso (en 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));
}
Kelly L
fuente
2
Hiciste lo contrario de lo que se te pidió, pero +1 para tu lambda-fu.
nurettin
IndexOfes bastante lento, será mejor que precalcules el mapeo inverso.
Vlad
5

Quería agregar mi clase estática que uso, para interoperar entre col index y col Label. Yo uso una respuesta aceptada modificada para mi 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;
    }
}

Usa esto como ...

30.ColumnLabel(); // "AD"
"AD".ColumnIndex(); // 30
t3dodson
fuente
4

si solo lo desea para una fórmula de celda sin código, aquí hay una fórmula para ello:

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

En 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;
JRL
fuente
3
private String getColumn(int c) {
    String s = "";
    do {
        s = (char)('A' + (c % 26)) + s;
        c /= 26;
    } while (c-- > 0);
    return s;
}

No es exactamente la base 26, no hay 0 en el sistema. Si existiera, 'Z' sería seguido por 'BA' y no por 'AA'.

hule
fuente
3

En perl, para una 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;
}
Myforwik
fuente
2

Refinando la solución original (en 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
fuente
2

Aquí hay una versión de 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;
    }
Robar
fuente
2

Solución 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;
};
Aliado
fuente
1
Pruebe los índices 26 y 27. Está muy cerca, pero fuera por uno.
Eric
valor = Math.floor (valor / 26); debe ser value = Math.ceil (value / 26) - 1;
Henry Liu el
2

Estos mis códigos para convertir un número específico (índice comienza desde 1) a la columna de 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;
    }

Mi prueba de unidad:

    [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));
    }
Stevanus
fuente
+1 para mostrar cuál es la referencia de celda máxima en sus pruebas (XFD): no creería lo difícil que es encontrar esa información en la web.
controlbox
2

Aunque llego tarde al juego, la respuesta de Graham está lejos de ser óptima. En particular, no tiene que usar modulo, invocar ToString()y aplicar el (int)elenco. Teniendo en cuenta que en la mayoría de los casos en el mundo de C # comenzaría a numerar desde 0, aquí está mi revisión:

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;
}
Herman Kan
fuente
2

Otra forma de VBA

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

Aquí está mi implementación súper tardía en PHP. Este es recursivo. Lo escribí justo antes de encontrar esta publicación. Quería ver si otros ya habían resuelto este 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;
}
Ian Atkin
fuente
1

Estoy tratando de hacer lo mismo en Java ... Escribí el siguiente 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;
}

Ahora, una vez que lo ejecuté con columnNumber = 29, me da el resultado = "CA" (en lugar de "AC") ¿algún comentario que me falta? Sé que puedo revertirlo con StringBuilder ... Pero mirando la respuesta de Graham, estoy un poco confundido ...

Hasan
fuente
Graham dice: columnName = Convert.ToChar(65 + modulo).ToString() + columnName(es decir, valor + ColName). Hasan dice: columnName += val;(es decir, ColName + valor)
mcalex
Estás agregando el nuevo personaje en lugar de anteponerlo. Debe ser columnName = columnName + val.
Domenic D.
1

Coincise y elegante versión 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
Iwan B.
fuente
1

Esta es la pregunta a la que todos los demás, así como Google redirigen, así que estoy publicando esto aquí.

Muchas de estas respuestas son correctas pero demasiado engorrosas para situaciones simples como cuando no tiene más de 26 columnas. Si tiene alguna duda sobre si podría entrar en columnas de doble carácter, ignore esta respuesta, pero si está seguro de que no lo hará, entonces podría hacerlo tan simple como esto en 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);
}

Diablos, si estás seguro de lo que estás pasando, incluso podrías eliminar la validación y usar esta línea:

(char)('A' + index)

Esto será muy similar en muchos idiomas para que pueda adaptarlo según sea necesario.

Nuevamente, solo use esto si está 100% seguro de que no tendrá más de 26 columnas .

Vlad Schnakovszki
fuente
1

Gracias por las respuestas aquí !! me ayudó a desarrollar estas funciones auxiliares para interactuar con la API de Google Sheets en la que estoy trabajando en Elixir / Phoenix

esto es lo que se me ocurrió (probablemente podría usar alguna validación adicional y manejo de errores)

En 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

Y la función 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

Y algunas pruebas:

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
phlare
fuente