Comment convertir un numéro de colonne (par exemple 127) en une colonne Excel (par exemple AA)

475

Comment convertir un nombre numérique en nom de colonne Excel en C # sans utiliser l'automatisation pour obtenir la valeur directement à partir d'Excel.

Excel 2007 a une plage possible de 1 à 16384, qui est le nombre de colonnes qu'il prend en charge. Les valeurs résultantes doivent être sous la forme de noms de colonnes Excel, par exemple A, AA, AAA etc.

robertkroll
la source
1
Sans oublier qu'il y a des limites dans le nombre de colonnes disponibles. Par exemple * Excel 2003 (v11) va jusqu'à IV, 2 ^ 8 ou 256 colonnes). * Excel 2007 (v12) monte jusqu'à XFD, 2 ^ 14 ou 16384 colonnes.
non tranché
Cette question est étiquetée C # et excelle. Je signale cette question comme dépassée, car nous vivons en 2016 et il y a EPPLUS . Une bibliothèque C # couramment utilisée pour créer des feuilles de calcul Excel avancées sur le serveur. Qui est disponible sous: GNU Library General Public License (LGPL). En utilisant EPPlus, vous pouvez facilement obtenir la chaîne de colonne.
Tony_KiloPapaMikeGolf
Notez que les limites de ligne et de colonne dépendent davantage du format de fichier que de la version Excel et peuvent être différentes pour chaque classeur. Ils peuvent changer même pour le même classeur s'il est enregistré dans un format plus ancien ou plus récent.
Slai

Réponses:

902

Voici comment je le fais:

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
la source
65
Ce code fonctionne bien. Il suppose que la colonne A est columnNumber 1. J'ai dû faire un changement rapide pour tenir compte de mon système en utilisant la colonne A comme columnNumber 0. J'ai changé la ligne int dividend en int dividend = columnNumber + 1; Keith
Keith Sirmons
14
@Jduv vient de l'essayer en utilisant StringBuilder. Cela prend environ deux fois plus de temps. Il s'agit d'une chaîne très courte (max 3 caractères - Excel 2010 monte jusqu'à la colonne XFD), donc maximum de 2 concaténations de chaînes. (J'ai utilisé 100 itérations pour traduire les entiers 1 à 16384, c'est-à-dire les colonnes Excel A à XFD, comme test).
Graham
18
Pour une meilleure compréhension, je remplacerais le 65 par 'A'
Stef Heyenrath
11
Je pense qu'il serait préférable d'utiliser 'A' au lieu de 65. Et 26 pourrait être évalué comme ('Z' - 'A' + 1), par exemple: const int AlphabetLength = 'Z' - 'A' + 1;
Denis Gladkiy
5
Bien que je sois en retard dans le jeu, le code est loin d'être optimal. En particulier, vous n'avez pas besoin d'utiliser le modulo, d'appeler ToString()et d'appliquer le (int)casting. Étant donné que dans la plupart des cas dans le monde C #, vous commenceriez à numéroter à partir de 0, voici ma révision: <! - langue: c # -> chaîne statique publique GetColumnName (int index) // base zéro {const byte BASE = 'Z «-« A »+ 1; nom de chaîne = String.Empty; do {name = Convert.ToChar ('A' + index% BASE) + name; index = index / BASE - 1; } while (index> = 0); nom de retour; }
Herman Kan
63

Si quelqu'un a besoin de le faire dans Excel sans VBA, voici un moyen:

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

où colNum est le numéro de colonne

Et 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
vzczc
la source
2
Exemple: = SUBSTITUTE (TEXT (ADDRESS (1,1000,4), ""), "1", "")
Dolph
Oui, j'utilise Excel dans un environnement local où; est utilisé à la place de, pour séparer les arguments de fonction dans Excel. Merci de l'avoir signalé.
vzczc
2
Je l'ai fait sans la fonction TEXT. À quoi sert la fonction TEXTE?
dayuloli
2
@dayuloli Il y a longtemps que cette réponse n'a pas été écrite, vous avez raison, la fonction TEXTE ne sert à rien ici. Met à jour la réponse.
vzczc
21

Désolé, c'est Python au lieu de C #, mais au moins les résultats sont corrects:

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
la source
Oui, j'ai voté, ne postez pas Python lorsque la question est C #. Et oui, plus de gens devraient faire ça.
KyloRen
1
Le titre de la question n'implique pas C #, donc beaucoup de gens peuvent venir ici qui n'attendent pas C #. Par conséquent, merci pour le partage en Python!
Tim-Erwin
20

Vous pourriez avoir besoin d'une conversion dans les deux sens, par exemple à partir d'une adresse de colonne Excel comme AAZ en entier et de n'importe quel entier en Excel. Les deux méthodes ci-dessous feront exactement cela. Suppose une indexation basée sur 1, le premier élément de vos "tableaux" est le numéro d'élément 1. Aucune limite de taille ici, vous pouvez donc utiliser des adresses comme ERROR et ce serait le numéro de colonne 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
la source
13

J'ai découvert une erreur dans mon premier post, j'ai donc décidé de m'asseoir et de faire le calcul. Ce que j'ai trouvé, c'est que le système de numérotation utilisé pour identifier les colonnes Excel n'est pas un système de base 26, comme l'a signalé une autre personne. Considérez ce qui suit dans la base 10. Vous pouvez également le faire avec les lettres de l'alphabet.

Espace: ......................... S1, S2, S3: S1, S2, S3
............ ........................ 0, 00, 000: .. A, AA, AAA
............. ....................... 1, 01, 001: .. B, AB, AAB
.............. ......................…,…,…: ..…,…,…
............... ..................... 9, 99, 999: .. Z, ZZ, ZZZ
Nombre total d'états dans l'espace: 10, 100, 1000: 26, 676, 17576
Total des États: ............... 1110 ................ 18278

Colonnes de nombres Excel dans les espaces alphabétiques individuels en utilisant la base 26. Vous pouvez voir qu'en général, la progression de l'espace d'états est a, a ^ 2, a ^ 3,… pour une base a, et le nombre total d'états est a + a ^ 2 + a ^ 3 +….

Supposons que vous vouliez trouver le nombre total d'états A dans les N premiers espaces. La formule pour ce faire est A = (a) (a ^ N - 1) / (a-1). Ceci est important car nous devons trouver l'espace N qui correspond à notre indice K. Si je veux savoir où K se situe dans le système numérique, je dois remplacer A par K et résoudre pour N. La solution est N = log { base a} (A (a-1) / a +1). Si j'utilise l'exemple de a = 10 et K = 192, je sais que N = 2,23804…. Cela me dit que K se trouve au début du troisième espace car il est un peu plus grand que deux.

La prochaine étape consiste à trouver exactement dans quelle mesure nous nous trouvons dans l'espace actuel. Pour trouver cela, soustrayez de K le A généré en utilisant l'étage de N. Dans cet exemple, l'étage de N est deux. Ainsi, A = (10) (10 ^ 2 - 1) / (10-1) = 110, comme prévu lorsque vous combinez les états des deux premiers espaces. Cela doit être soustrait de K car ces 110 premiers états auraient déjà été pris en compte dans les deux premiers espaces. Cela nous laisse avec 82 états. Donc, dans ce système numérique, la représentation de 192 dans la base 10 est 082.

Le code C # utilisant un index de base de zéro est

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

// Old Post

Une solution à base zéro 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;
    }
John
la source
ooh je ne sais pas pourquoi mais j'aime cette solution. Rien d'extraordinaire, juste une bonne utilisation de la logique ... du code facilement lisible pour les niveaux de programmeur. Une chose cependant, je crois que sa meilleure pratique consiste à spécifier une chaîne vide en C # en tant que chaîne range = string.Empty;
Type anonyme
Oui, très belle explication. Mais pourriez-vous également dire que ce n'est pas la base 27 non plus? Votre explication le montre lors de l'étude, mais une mention rapide en haut peut faire gagner du temps à quelques autres personnes.
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;

Mise à jour : Peter le commentaire de est juste. C'est ce que j'obtiens pour écrire du code dans le navigateur. :-) Ma solution ne compilait pas, il manquait la lettre la plus à gauche et elle construisait la chaîne dans l'ordre inverse - tout est maintenant corrigé.

Mis à part les bugs, l'algorithme est en train de convertir un nombre de la base 10 à la base 26.

Mise à jour 2 : Joel Coehoorn a raison - le code ci-dessus renverra AB pour 27. S'il s'agissait d'un vrai nombre de base 26, AA serait égal à A et le nombre suivant après Z serait 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;
Franci Penov
la source
Le code ne se compilera pas (sCol non initialisé). Si c'est le cas, il ne donnera pas la bonne réponse.
Peter
5
CETTE RÉPONSE EST FAUX. Base26 n'est pas assez bon. Pensez à ce qui se passe lorsque vous passez de Z à AA. Si A équivaut au chiffre 0, alors c'est comme envelopper de 9 à 00. Si c'est en 1 chiffre, c'est comme envelopper de 9 à 11.
Joel Coehoorn
4
Je ne suis pas clair après les mises à jour ... l'un des algorithmes est-il maintenant correct? Et si oui, lequel, le second? Je modifierais ceci et le
rendrais
10

Facile avec récursivité.

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
la source
1
.. ou une boucle. Il n'y a aucune vraie raison d'utiliser la récursivité ici.
Blorgbeard est sorti
1
Ce n'est pas seulement la base 26, donc la solution récursive est beaucoup plus simple.
Joel Coehoorn
Cette routine ne fonctionne pas réellement. Par exemple, GetStandardExcelColumnName (26) renvoie @ GetStandardExcelColumnName (52) renvoie B @
sgmoore
1
la solution la plus claire par opposition à la solution "la plus simple" est la meilleure.
Type anonyme
9

Il suffit de lancer une simple implémentation C # à deux lignes en utilisant la récursivité, car toutes les réponses ici semblent beaucoup plus compliquées que nécessaires.

/// <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
la source
8

..Et converti en 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
la source
Utiliser ord('A')au lieu de 65.
Mulli
8

Je suis surpris que toutes les solutions jusqu'à présent contiennent soit une itération soit une récursivité.

Voici ma solution qui s'exécute en temps constant (pas de boucles). Cette solution fonctionne pour toutes les colonnes Excel possibles et vérifie que l'entrée peut être transformée en colonne Excel. Les colonnes possibles sont dans la plage [A, XFD] ou [1, 16384]. (Cela dépend de votre version d'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
la source
2
FYI: La réponse de @ Graham (et probablement les autres) est plus générale que la vôtre: ils prennent en charge 4+ caractères dans les noms de colonne. Et c'est précisément pourquoi ils sont itératifs.
bernard paulus
En fait, s'ils utilisaient des nombres entiers illimités et non ints, le nom de la colonne résultante pourrait être arbitrairement long (c'est le cas de la réponse python, par exemple)
bernard paulus
1
Si mes données sont trop volumineuses pour une feuille de calcul de 16 384 colonnes, je me tire une balle dans la tête. Quoi qu'il en soit, Excel ne prend même pas en charge toutes les colonnes à trois lettres possibles (il coupe à XFD en laissant 1894 colonnes). En ce moment en tout cas. Je mettrai à jour ma réponse à l'avenir au besoin.
user2023861
:) ne le savait pas! Mon commentaire portait sur les propriétés théoriques des différents algorithmes.
bernard paulus
Celui-ci est probablement la solution la plus simple et la plus claire.
Juan
8

Cette réponse 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
la source
6

Même implémentation 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
la source
5

Après avoir regardé toutes les versions fournies ici, je suis descendu pour en faire une moi-même, en utilisant la récursivité.

Voici ma version 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
la source
5

Un peu tard pour le jeu, mais voici le code que j'utilise (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
la source
2
Vous avez fait l'inverse de ce qui vous a été demandé, mais +1 pour votre lambda-fu.
nurettin
IndexOfest assez lent, vous feriez mieux de précalculer le mappage inverse.
Vlad
5

Je voulais ajouter ma classe statique que j'utilise, pour l'interopérabilité entre col index et col Label. J'utilise une réponse acceptée modifiée pour ma méthode 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;
    }
}

Utilisez ceci comme ...

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

si vous le souhaitez uniquement pour une formule de cellule sans code, voici une formule:

IF(COLUMN()>=26,CHAR(ROUND(COLUMN()/26,1)+64)&CHAR(MOD(COLUMN(),26)+64),CHAR(COLUMN()+64))
Matt Lewis
la source
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
la source
3
private String getColumn(int c) {
    String s = "";
    do {
        s = (char)('A' + (c % 26)) + s;
        c /= 26;
    } while (c-- > 0);
    return s;
}

Ce n'est pas exactement la base 26, il n'y a pas de 0 dans le système. S'il y en avait, «Z» serait suivi de «BA» et non de «AA».

Rubberduckie
la source
3

En perl, pour une entrée 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
la source
2

Affiner la solution d'origine (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
la source
2

Voici une version 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;
    }
Rob
la source
2

Solution 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;
};
Allié
la source
1
Essayez les index 26 et 27. C'est très proche, mais décalé d'un.
Eric
value = Math.floor (valeur / 26); devrait être value = Math.ceil (value / 26) - 1;
Henry Liu
2

Ces mes codes pour convertir un nombre spécifique (début de l'index à partir de 1) en colonne 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;
    }

Mon test unitaire:

    [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
la source
+1 pour montrer quelle est la référence maximale des cellules dans vos tests (XFD) - vous ne croiriez pas à quel point il est difficile de trouver ces informations sur le Web.
controlbox
2

Bien que je sois en retard dans le match, la réponse de Graham est loin d'être optimale. En particulier, vous n'avez pas besoin d'utiliser le modulo, d'appeler ToString()et d'appliquer le (int)casting. Étant donné que dans la plupart des cas dans le monde C #, vous commenceriez à numéroter à partir de 0, voici ma révision:

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
la source
2

Une autre façon VBA

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

Voici mon implémentation super tardive en PHP. Celui-ci est récursif. Je l'ai écrit juste avant de trouver ce post. Je voulais voir si d'autres avaient déjà résolu ce problème ...

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
la source
1

J'essaie de faire la même chose en Java ... J'ai écrit le code suivant:

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

Maintenant, une fois que je l'ai exécuté avec columnNumber = 29, cela me donne le résultat = "CA" (au lieu de "AC") des commentaires sur ce qui me manque? Je sais que je peux l'inverser par StringBuilder .... Mais en regardant la réponse de Graham, je suis un peu confus ....

Hasan
la source
Graham dit: columnName = Convert.ToChar(65 + modulo).ToString() + columnName(ie valeur + ColName). Hasan dit: columnName += val;(c'est-à-dire ColName + valeur)
mcalex
Vous ajoutez le nouveau personnage au lieu de le faire précéder. Devrait l'être columnName = columnName + val.
Domenic D.24
1

Version Rubis coïncise et élégante :

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.
la source
1

C'est la question que tous les autres ainsi que la redirection de Google, donc je poste ceci ici.

Beaucoup de ces réponses sont correctes mais trop lourdes pour des situations simples comme lorsque vous n'avez pas plus de 26 colonnes. Si vous avez le moindre doute quant à savoir si vous pouvez entrer dans des colonnes à deux caractères, ignorez cette réponse, mais si vous êtes sûr de ne pas le faire, vous pouvez le faire aussi simplement que cela 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);
}

Heck, si vous êtes sûr de ce que vous passez, vous pouvez même supprimer la validation et utiliser cette ligne:

(char)('A' + index)

Ce sera très similaire dans de nombreuses langues afin que vous puissiez l'adapter au besoin.

Encore une fois, utilisez-le uniquement si vous êtes sûr à 100% que vous n'aurez pas plus de 26 colonnes .

Vlad Schnakovszki
la source
1

Merci pour les réponses ici !! m'a aidé à trouver ces fonctions d'aide pour une interaction avec l'API Google Sheets sur laquelle je travaille dans Elixir / Phoenix

voici ce que j'ai trouvé (pourrait probablement utiliser une validation supplémentaire et une gestion des erreurs)

Dans 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

Et la fonction inverse:

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

Et quelques tests:

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
la source