Come convertire un numero di colonna (ad es. 127) in una colonna di Excel (ad es. AA)


475

Come si converte un numero numerico in un nome di colonna di Excel in C # senza usare l'automazione per ottenere il valore direttamente da Excel.

Excel 2007 ha un intervallo possibile compreso tra 1 e 16384, ovvero il numero di colonne supportate. I valori risultanti dovrebbero essere sotto forma di nomi di colonne Excel, ad esempio A, AA, AAA ecc.


1
Senza dimenticare che ci sono limiti nel numero di colonne disponibili. Ad esempio * Excel 2003 (v11) arriva fino a IV, 2 ^ 8 o 256 colonne). * Excel 2007 (v12) arriva fino a XFD, 2 ^ 14 o 16384 colonne.
affettato


Questa domanda è taggata C # ed Excel. Contrassegno questa domanda come obsoleta, perché viviamo nel 2016 e c'è EPPLUS . Una libreria C # comunemente usata per creare fogli di calcolo Excel avanzati sul server. Che è reso disponibile sotto: GNU Library General Public License (LGPL). Utilizzando EPPlus puoi ottenere facilmente la stringa di colonne.
Tony_KiloPapaMikeGolf

Si noti che i limiti di riga e colonna dipendono più dal formato del file rispetto alla versione di Excel e possono essere diversi per ogni cartella di lavoro. Possono cambiare anche per la stessa cartella di lavoro se viene salvato nel formato più vecchio o più recente.
Slai,

Risposte:


902

Ecco come lo faccio:

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
Questo codice funziona bene. Presuppone che la colonna A sia columnNumber 1. Ho dovuto apportare una rapida modifica per tenere conto del mio sistema utilizzando la colonna A come columnNumber 0. Ho modificato la riga int dividendo in int dividend = columnNumber + 1; Keith
Keith Sirmons il

14
@Jduv l'ho appena provato usando StringBuilder. Ci vuole circa il doppio del tempo. È una stringa molto corta (massimo 3 caratteri - Excel 2010 arriva alla colonna XFD), quindi un massimo di 2 concatenazioni di stringhe. (Ho usato 100 iterazioni per tradurre gli interi da 1 a 16384, ovvero le colonne Excel da A a XFD, come test).
Graham,

18
Per una migliore comprensione, sostituirei il 65 con "A"
Stef Heyenrath l'

11
Penso che sarebbe meglio usare 'A' invece di 65. E 26 potrebbero essere valutati come ('Z' - 'A' + 1), ad esempio: const int AlphabetLength = 'Z' - 'A' + 1;
Denis Gladkiy,

5
Anche se sono in ritardo al gioco, il codice è lungi dall'essere ottimale. In particolare, non è necessario utilizzare modulo, chiamare ToString()e applicare il (int)cast. Considerando che nella maggior parte dei casi nel mondo C # inizieresti la numerazione da 0, ecco la mia revisione: <! - lingua: c # -> stringa statica pubblica GetColumnName (int index) // a base zero {const byte BASE = 'Z '-' A '+ 1; string name = String.Empty; do {name = Convert.ToChar ('A' + indice% BASE) + nome; indice = indice / BASE - 1; } while (indice> = 0); nome di ritorno; }
Herman Kan,

63

Se qualcuno ha bisogno di farlo in Excel senza VBA, ecco un modo:

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

dove colNum è il numero di colonna

E in 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
Esempio: = SOSTITUTIVO (TESTO (INDIRIZZO (1.1000,4), ""), "1", "")
Dolph,

Sì, utilizzo Excel in una locale in cui; viene utilizzato al posto di, per separare gli argomenti delle funzioni in Excel. Grazie per averlo segnalato.
vzczc,

2
L'ho fatto senza la funzione TEXT. Qual è lo scopo della funzione TEXT?
Dayuloli,

2
@dayuloli Molto tempo da quando questa risposta è stata scritta, hai ragione, la funzione TESTO non ha uno scopo qui. Aggiornerà la risposta.
vzczc,

21

Siamo spiacenti, questo è Python anziché C #, ma almeno i risultati sono corretti:

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

Sì, ho votato verso il basso, non pubblicare Python quando la domanda è C #. E sì, più persone dovrebbero farlo.
KyloRen,

1
Il titolo della domanda non implica C #, quindi molte persone possono venire qui che non si aspettano C #. Quindi, grazie per aver condiviso in Python!
Tim-Erwin,

20

Potrebbe essere necessaria la conversione in entrambi i modi, ad es. Dall'indirizzo di colonna di Excel come AAZ in numero intero e da qualsiasi numero intero in Excel. I due metodi seguenti faranno proprio questo. Presuppone 1 indicizzazione basata, il primo elemento nelle "matrici" è il numero elemento 1. Qui non ci sono limiti alle dimensioni, quindi è possibile utilizzare indirizzi come ERRORE e questo sarebbe il numero di colonna 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

Ho scoperto un errore nel mio primo post, quindi ho deciso di sedermi e fare i conti. Quello che ho scoperto è che il sistema numerico utilizzato per identificare le colonne di Excel non è un sistema di base 26, come pubblicato da un'altra persona. Considera quanto segue nella base 10. Puoi anche farlo con le lettere dell'alfabeto.

Spazio: ......................... S1, S2, S3: S1, S2, S3
............ ........................ 0, 00, 000: .. A, AA, AAA
............. ....................... 1, 01, 001: .. B, AB, AAB
.............. ...................... ..., ..., ...: .. ..., ..., ...
............... ..................... 9, 99, 999: .. Z, ZZ, ZZZ
Stati totali nello spazio: 10, 100, 1000: 26, 676, 17576
Stati totali: ............... 1110 ................ 18278

Excel numera le colonne nei singoli spazi alfabetici usando la base 26. Puoi vedere che in generale, la progressione dello spazio degli stati è a, a ^ 2, a ^ 3, ... per qualche base a, e il numero totale di stati è a + a ^ 2 + a ^ 3 +….

Supponiamo di voler trovare il numero totale di stati A nei primi N spazi. La formula per farlo è A = (a) (a ^ N - 1) / (a-1). Questo è importante perché dobbiamo trovare lo spazio N che corrisponde al nostro indice K. Se voglio scoprire dove si trova K nel sistema numerico, devo sostituire A con K e risolvere per N. La soluzione è N = log { base a} (A (a-1) / a +1). Se uso l'esempio di a = 10 e K = 192, so che N = 2.23804…. Questo mi dice che K si trova all'inizio del terzo spazio poiché è leggermente più grande di due.

Il prossimo passo è trovare esattamente quanto siamo lontani nello spazio attuale. Per trovare questo, sottrarre da K la A generata usando il piano di N. In questo esempio, il pavimento di N è due. Quindi, A = (10) (10 ^ 2 - 1) / (10-1) = 110, come previsto quando si combinano gli stati dei primi due spazi. Questo deve essere sottratto da K perché questi primi 110 stati sarebbero già stati contabilizzati nei primi due spazi. Questo ci lascia con 82 stati. Quindi, in questo sistema di numeri, la rappresentazione di 192 nella base 10 è 082.

Il codice C # che utilizza un indice di base pari a 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;
    }

// Vecchia posta

Una soluzione a base zero in 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 non so perché ma mi piace questa soluzione. Niente di speciale solo un buon uso della logica ... codice facilmente leggibile per i livelli di programmatore. Una cosa però, credo che sia meglio specificare una stringa vuota in C # come string range = string.Empty;
Tipo anonimo

Sì, spiegazione molto bella. Ma potresti anche affermare che non è neanche la base 27? La tua spiegazione mostra questo quando studiato, ma una breve menzione in alto può far risparmiare qualche altra persona un po 'di 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;

Aggiornamento : il commento di Peter è giusto. Questo è quello che ottengo scrivendo il codice nel browser. :-) La mia soluzione non era la compilazione, mancava la lettera più a sinistra e stava costruendo la stringa in ordine inverso - tutto ora risolto.

A parte i bug, l'algoritmo sta sostanzialmente convertendo un numero dalla base 10 alla base 26.

Aggiornamento 2 : Joel Coehoorn ha ragione: il codice sopra restituirà AB per 27. Se fosse il numero di base 26 reale, AA sarebbe uguale ad A e il numero successivo dopo Z sarebbe 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;

Il codice non verrà compilato (sCol non inizializzato). Se lo fa, non darà la risposta giusta.
Peter,

5
QUESTA RISPOSTA È ERRATA. Base26 non è abbastanza buono. Pensa a cosa succede quando passi dalla Z alla AA. Se A è equivalente alla cifra 0, allora è come racchiudere da 9 a 00. Se sono le 1 cifre, è come avvolgere da 9 a 11.
Joel Coehoorn,

4
Non sono chiaro dopo gli aggiornamenti ... uno degli algoritmi ora è corretto? E se sì, quale, la seconda?
Modificherei

10

Facile con ricorsione.

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
.. o un ciclo. Non c'è motivo reale per usare la ricorsione qui.
Blorgbeard esce l'

1
Non è solo la base 26, quindi la soluzione ricorsiva è molto più semplice.
Joel Coehoorn,

Questa routine in realtà non funziona. Ad esempio GetStandardExcelColumnName (26) restituisce @ GetStandardExcelColumnName (52) restituisce B @
sgmoore

1
la soluzione più chiara rispetto alla "più semplice" è la migliore.
Tipo anonimo

9

Basta lanciare una semplice implementazione C # a due righe usando la ricorsione, perché tutte le risposte qui sembrano molto più complicate del necessario.

/// <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 convertito in php:

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

Usa ord('A')invece di 65.
Mulli

8

Sono sorpreso che tutte le soluzioni finora contengano iterazione o ricorsione.

Ecco la mia soluzione che funziona a tempo costante (senza loop). Questa soluzione funziona per tutte le possibili colonne di Excel e verifica che l'input possa essere trasformato in una colonna di Excel. Le colonne possibili sono nell'intervallo [A, XFD] o [1, 16384]. (Dipende dalla tua versione di 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
Cordiali saluti: la risposta di @ Graham (e probabilmente le altre) è più generale della tua: supportano più di 4 caratteri nei nomi delle colonne. Ed è proprio per questo che sono iterativi.
bernard paulus,

Infatti, se usassero numeri interi illimitati e non ints, il nome della colonna risultante potrebbe essere arbitrariamente lungo (questo è il caso della risposta python, per esempio)
bernard paulus

1
Se i miei dati sono mai troppo grandi per un foglio di calcolo da 16.384 colonne, mi sparerò alla testa. Comunque, Excel non supporta nemmeno tutte le possibili colonne di tre lettere (si interrompe in XFD tralasciando 1.894 colonne). In questo momento comunque. Aggiornerò la mia risposta in futuro, se necessario.
user2023861

:) non lo sapevo! Il mio commento era sulle proprietà teoriche dei diversi algoritmi.
bernard paulus,

Questa è probabilmente la soluzione più semplice e chiara.
Juan,

8

Questa risposta è in 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

Stessa implementazione in 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

Dopo aver esaminato tutte le versioni fornite qui, sono sceso a farne uno da solo, usando la ricorsione.

Ecco la mia versione di 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

Un po 'in ritardo al gioco, ma ecco il codice che uso (in 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
Hai fatto l'inverso di ciò che ti è stato chiesto, ma +1 per il tuo lambda-fu.
Nurettin,

IndexOfè piuttosto lento, è meglio precalcare la mappatura inversa.
Vlad

5

Volevo aggiungere la mia classe statica che uso, per l'interoperabilità tra indice col e etichetta col. Uso una risposta accettata modificata per il mio metodo 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 questo come ...

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

4

se lo vuoi solo per una formula di cella senza codice, ecco una formula per esso:

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

4

A Delfi (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;
}

Non è esattamente la base 26, non c'è 0 nel sistema. Se ci fosse, "Z" sarebbe seguito da "BA" e non da "AA".


3

In perl, per un input di 1 (A), 27 (AA), ecc.

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

Affinamento della soluzione originale (in 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

Ecco una versione di 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

Soluzione 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
Prova gli indici 26 e 27. È molto vicino, ma fuori da uno.
Eric,

valore = Math.floor (valore / 26); dovrebbe essere value = Math.ceil (value / 26) - 1;
Henry Liu,

2

Questi i miei codici per convertire un numero specifico (inizio dell'indice da 1) alla colonna 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;
    }

Il mio test unitario:

    [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 per mostrare qual è il riferimento massimo della cella nei tuoi test (XFD) - non crederesti quanto sia difficile trovare tali informazioni sul web.
controlbox

2

Anche se sono in ritardo al gioco, la risposta di Graham è lungi dall'essere ottimale. In particolare, non è necessario utilizzare modulo, chiamare ToString()e applicare (int)cast. Considerando che nella maggior parte dei casi nel mondo C # inizieresti la numerazione da 0, ecco la mia revisione:

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

Un altro modo VBA

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

1

Ecco la mia implementazione tardiva in PHP. Questo è ricorsivo. L'ho scritto poco prima di trovare questo post. Volevo vedere se altri avevano già risolto questo 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

Sto provando a fare la stessa cosa in Java ... Ho scritto il seguente codice:

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

Ora, una volta eseguito con columnNumber = 29, mi dà il risultato = "CA" (invece di "AC") eventuali commenti che cosa mi manca? So di poterlo invertire da StringBuilder .... Ma guardando la risposta di Graham, sono un po 'confuso ....


Graham dice: columnName = Convert.ToChar(65 + modulo).ToString() + columnName(cioè valore + ColName). Hasan dice: columnName += val;(cioè ColName + valore)
mcalex,

Stai aggiungendo il nuovo personaggio invece di anteporre. Dovrebbe essere columnName = columnName + val.
Domenic D.

1

Coincisa ed elegante versione 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

Questa è la domanda a cui reindirizzano tutti gli altri, così sto pubblicando questo qui.

Molte di queste risposte sono corrette ma troppo ingombranti per situazioni semplici come quando non hai più di 26 colonne. Se hai dei dubbi sul fatto che potresti entrare in colonne a doppio carattere, ignora questa risposta, ma se sei sicuro di no, allora potresti farlo in 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);
}

Diamine, se sei sicuro di ciò che stai passando, potresti persino rimuovere la convalida e utilizzare questo inline:

(char)('A' + index)

Questo sarà molto simile in molte lingue in modo da poterlo adattare secondo necessità.

Ancora una volta, usa questo solo se sei sicuro al 100% di non avere più di 26 colonne .


1

Grazie per le risposte qui !! mi ha aiutato a inventare queste funzioni di supporto per l'interazione con l'API di Google Sheets su cui sto lavorando in Elixir / Phoenix

ecco cosa mi è venuto in mente (probabilmente potrei usare un po 'di convalida extra e gestione degli errori)

Nell'elisir:

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 la funzione 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 alcuni test:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.