So konvertieren Sie eine Spaltennummer (z. B. 127) in eine Excel-Spalte (z. B. AA)


474

Wie konvertieren Sie eine numerische Zahl in einen Excel-Spaltennamen in C #, ohne die Automatisierung zu verwenden, um den Wert direkt aus Excel abzurufen?

Excel 2007 hat einen möglichen Bereich von 1 bis 16384, dh die Anzahl der unterstützten Spalten. Die resultierenden Werte sollten in Form von Excel-Spaltennamen vorliegen, z. B. A, AA, AAA usw.


1
Nicht zu vergessen, dass die Anzahl der verfügbaren Spalten begrenzt ist. ZB * Excel 2003 (v11) reicht bis zu IV, 2 ^ 8 oder 256 Spalten). * Excel 2007 (v12) reicht bis zu XFD-, 2 ^ 14- oder 16384-Spalten.
Nicht geschnitten


Diese Frage ist mit C # und Excel gekennzeichnet. Ich kennzeichne diese Frage als veraltet, weil wir 2016 leben und es EPPLUS gibt . Eine häufig verwendete C # -Bibliothek zum Erstellen erweiterter Excel-Tabellen auf dem Server. Was zur Verfügung gestellt wird unter: GNU Library General Public License (LGPL). Mit EPPlus können Sie die Spaltenzeichenfolge leicht abrufen.
Tony_KiloPapaMikeGolf

Beachten Sie, dass die Zeilen- und Spaltenbeschränkungen stärker vom Dateiformat als von der Excel-Version abhängen und für jede Arbeitsmappe unterschiedlich sein können. Sie können sich auch für dieselbe Arbeitsmappe ändern, wenn sie in einem älteren oder neueren Format gespeichert wird.
Slai

Antworten:


901

So mache ich das:

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
Dieser Code funktioniert gut. Es wird davon ausgegangen, dass Spalte A Spaltennummer 1 ist. Ich musste eine schnelle Änderung vornehmen, um mein System mit Spalte A als Spaltennummer 0 zu berücksichtigen. Ich habe die Zeile int dividend in int dividend = columnNumber + 1 geändert. Keith
Keith Sirmons

14
@Jduv hat es gerade mit ausprobiert StringBuilder. Es dauert ungefähr doppelt so lange. Es ist eine sehr kurze Zeichenfolge (maximal 3 Zeichen - Excel 2010 geht bis zur Spalte XFD), also maximal 2 Zeichenfolgenverkettungen. (Ich habe 100 Iterationen der Übersetzung der ganzen Zahlen 1 bis 16384, dh der Excel-Spalten A bis XFD, als Test verwendet).
Graham

18
Zum besseren Verständnis würde ich die 65 durch 'A' ersetzen
Stef Heyenrath

11
Ich denke, es wäre besser, 'A' anstelle von 65 zu verwenden. Und 26 könnte als ('Z' - 'A' + 1) bewertet werden, zum Beispiel: const int AlphabetLength = 'Z' - 'A' + 1;
Denis Gladkiy

5
Obwohl ich zu spät zum Spiel komme, ist der Code alles andere als optimal. Insbesondere müssen Sie die Besetzung nicht verwenden modulo, aufrufen ToString()und anwenden (int). In Anbetracht der Tatsache, dass Sie in der C # -Welt in den meisten Fällen mit der Nummerierung von 0 beginnen würden, ist hier meine Revision: <! - Sprache: c # -> öffentliche statische Zeichenfolge GetColumnName (int index) // nullbasiert {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); Name zurückgeben; }
Herman Kan

63

Wenn jemand dies in Excel ohne VBA tun muss, gibt es folgende Möglichkeiten:

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

Dabei ist colNum die Spaltennummer

Und 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
Beispiel: = SUBSTITUT (TEXT (ADRESSE (1.1000,4), ""), "1", "")
Dolph

Ja, ich verwende Excel in einem Gebietsschema, in dem; wird anstelle von verwendet, um Funktionsargumente in Excel zu trennen. Vielen Dank für den Hinweis.
vzczc

2
Ich habe dies ohne die TEXT-Funktion gemacht. Was ist der Zweck der TEXT-Funktion?
Dayuloli

2
@dayuloli Seit diese Antwort geschrieben wurde, haben Sie Recht, die TEXT-Funktion erfüllt hier keinen Zweck. Aktualisiert die Antwort.
VZCZC

21

Entschuldigung, dies ist Python anstelle von C #, aber zumindest die Ergebnisse sind korrekt:

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

Ja, ich habe abgestimmt, poste Python nicht, wenn die Frage C # ist. Und ja, mehr Leute sollten das tun.
KyloRen

1
Der Titel der Frage impliziert nicht C #, so dass möglicherweise so viele Leute hierher kommen, die C # nicht erwarten. Vielen Dank für das Teilen in Python!
Tim-Erwin

20

Möglicherweise müssen Sie in beide Richtungen konvertieren, z. B. von einer Excel-Spaltenadresse wie AAZ in eine Ganzzahl und von einer beliebigen Ganzzahl in Excel. Die beiden folgenden Methoden werden genau das tun. Angenommen, eine 1-basierte Indizierung ist das erste Element in Ihren "Arrays" Element Nummer 1. Hier gibt es keine Größenbeschränkungen. Sie können also Adressen wie ERROR verwenden, und das wäre die Spaltennummer 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

Ich habe in meinem ersten Beitrag einen Fehler entdeckt und mich entschlossen, mich hinzusetzen und zu rechnen. Was ich gefunden habe, ist, dass das Zahlensystem, das zur Identifizierung von Excel-Spalten verwendet wird, kein Basissystem ist, wie eine andere Person geschrieben hat. Beachten Sie Folgendes in Basis 10. Sie können dies auch mit den Buchstaben des Alphabets tun.

Leerzeichen: ......................... S1, S2, S3: S1, S2, S3
............ ........................ 0, 00, 000: .. A, AA, AAA
............. ....................... 1, 01, 001: .. B, AB, AAB
.............. ......................…,…,…: ..…,…,…
............... ..................... 9, 99, 999: .. Z, ZZ, ZZZ
Gesamtzustände im Raum: 10, 100, 1000: 26, 676, 17576
Gesamtzustände: ............... 1110 ................ 18278

Excel-Zahlenspalten in den einzelnen alphabetischen Räumen unter Verwendung der Basis 26. Sie können sehen, dass der Fortschreiten des Zustandsraums im Allgemeinen a, a ^ 2, a ^ 3,… für einige Basis a ist und die Gesamtzahl der Zustände a + a ist ^ 2 + a ^ 3 +….

Angenommen, Sie möchten die Gesamtzahl der Zustände A in den ersten N Feldern ermitteln. Die Formel dafür lautet A = (a) (a ^ N - 1) / (a-1). Dies ist wichtig, weil wir den Raum N finden müssen, der unserem Index K entspricht. Wenn ich herausfinden will, wo K im Zahlensystem liegt, muss ich A durch K ersetzen und nach N lösen. Die Lösung ist N = log { Basis a} (A (a-1) / a +1). Wenn ich das Beispiel von a = 10 und K = 192 verwende, weiß ich, dass N = 2.23804…. Dies sagt mir, dass K am Anfang des dritten Feldes liegt, da es etwas größer als zwei ist.

Der nächste Schritt besteht darin, genau herauszufinden, wie weit wir uns im aktuellen Raum befinden. Um dies zu finden, subtrahieren Sie von K das A, das unter Verwendung des Bodens von N erzeugt wurde. In diesem Beispiel ist der Boden von N zwei. Also ist A = (10) (10 ^ 2 - 1) / (10-1) = 110, wie erwartet, wenn Sie die Zustände der ersten beiden Räume kombinieren. Dies muss von K subtrahiert werden, da diese ersten 110 Zustände bereits in den ersten beiden Räumen berücksichtigt worden wären. Dies lässt uns mit 82 Staaten. In diesem Zahlensystem ist die Darstellung von 192 in Basis 10 also 082.

Der C # -Code mit einem Basisindex von Null ist

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

// Alter Beitrag

Eine auf Null basierende Lösung 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;
    }

Oh, ich weiß nicht warum, aber ich mag diese Lösung. Nichts Besonderes, nur eine gute Verwendung der Logik ... leicht lesbarer Code für Programmiererebenen. Eines glaube ich jedoch, dass es die beste Vorgehensweise ist, eine leere Zeichenfolge in C # als Zeichenfolgenbereich = Zeichenfolge anzugeben. Leer;
Anonymer Typ

Ja, sehr schöne Erklärung. Aber können Sie auch einfach sagen, dass es sich nicht um Basis 27 handelt? Ihre Erklärung zeigt dies beim Studium, aber eine kurze Erwähnung oben kann einigen anderen Menschen Zeit sparen.
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;

Update : Peters Kommentar ist richtig. Das bekomme ich zum Schreiben von Code im Browser. :-) Meine Lösung wurde nicht kompiliert, es fehlte der Buchstabe ganz links und es wurde die Zeichenfolge in umgekehrter Reihenfolge erstellt - alles jetzt behoben.

Abgesehen von Fehlern konvertiert der Algorithmus im Grunde genommen eine Zahl von Basis 10 in Basis 26.

Update 2 : Joel Coehoorn hat recht - der obige Code gibt AB für 27 zurück. Wenn es eine echte Basis-26-Zahl wäre, wäre AA gleich A und die nächste Zahl nach Z wäre 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;

Code wird nicht kompiliert (sCol nicht initialisiert). Wenn dies der Fall ist, wird es nicht die richtige Antwort geben.
Peter

5
Diese Antwort ist falsch. Base26 ist nicht gut genug. Überlegen Sie, was passiert, wenn Sie von Z nach AA wechseln. Wenn A der 0-Ziffer entspricht, ist es wie ein Wrapping von 9 bis 00. Wenn es die 1-Ziffern sind, ist es wie ein Wrapping von 9 bis 11.
Joel Coehoorn

4
Ich bin mir nach den Updates nicht sicher ... ist einer der Algorithmen jetzt korrekt? Und wenn ja, welches, das zweite? Ich würde dies bearbeiten und es für die Nachwelt offensichtlich machen ...
JoeCool

10

Einfach mit Rekursion.

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
.. oder eine Schleife. Es gibt hier keinen wirklichen Grund, Rekursion zu verwenden.
Blorgbeard ist

1
Es ist nicht nur Basis 26, daher ist die rekursive Lösung viel einfacher.
Joel Coehoorn

Diese Routine funktioniert eigentlich nicht. Zum Beispiel gibt GetStandardExcelColumnName (26) @ GetStandardExcelColumnName (52) zurück B @
sgmoore

1
Die klarste im Gegensatz zur "einfachsten" Lösung ist die beste.
Anonymer Typ

9

Wirf einfach eine einfache zweizeilige C # -Implementierung mithilfe der Rekursion ein, da alle Antworten hier weitaus komplizierter als nötig erscheinen.

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

..Und konvertiert zu PHP:

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

Verwenden Sie ord('A')anstelle von 65.
Mulli

8

Ich bin überrascht, dass alle bisherigen Lösungen entweder Iteration oder Rekursion enthalten.

Hier ist meine Lösung, die in konstanter Zeit läuft (keine Schleifen). Diese Lösung funktioniert für alle möglichen Excel-Spalten und überprüft, ob die Eingabe in eine Excel-Spalte umgewandelt werden kann. Mögliche Spalten liegen im Bereich [A, XFD] oder [1, 16384]. (Dies hängt von Ihrer Excel-Version ab.)

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
Zu Ihrer Information: @ Grahams Antwort (und wahrscheinlich die anderen) sind allgemeiner als Ihre: Sie unterstützen mehr als 4 Zeichen in den Spaltennamen. Und genau deshalb sind sie iterativ.
Bernard Paulus

In der Tat, wenn sie unbegrenzte ganze Zahlen und nicht ints verwenden, könnte der resultierende Spaltenname beliebig lang sein (das ist zum Beispiel bei der Python-Antwort der Fall)
bernard paulus

1
Wenn meine Daten für eine Tabelle mit 16.384 Spalten jemals zu groß sind, schieße ich mir in den Kopf. Auf jeden Fall unterstützt Excel nicht einmal alle möglichen Spalten mit drei Buchstaben (bei XFD werden 1.894 Spalten weggelassen). Im Moment sowieso. Ich werde meine Antwort in Zukunft nach Bedarf aktualisieren.
user2023861

:) wusste das nicht! Mein Kommentar betraf die theoretischen Eigenschaften der verschiedenen Algorithmen.
Bernard Paulus

Dies ist wahrscheinlich die einfachste und klarste Lösung.
Juan

8

Diese Antwort ist 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

Gleiche Implementierung 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

Nachdem ich mir alle hier gelieferten Versionen angesehen hatte, entschloss ich mich, selbst eine mit Rekursion zu erstellen.

Hier ist meine vb.net Version:

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

Ein bisschen spät zum Spiel, aber hier ist der Code, den ich benutze (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
Sie haben das Gegenteil von dem getan, was gefragt wurde, aber +1 für Ihr Lambda-Fu.
Nurettin

IndexOfist ziemlich langsam, Sie sollten die umgekehrte Zuordnung besser vorberechnen.
Vlad

5

Ich wollte meine statische Klasse einfügen, die ich zum Interoping zwischen col index und col Label verwende. Ich verwende eine modifizierte akzeptierte Antwort für meine ColumnLabel-Methode

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

Verwenden Sie dies wie ...

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

4

Wenn Sie es nur für eine Zellenformel ohne Code möchten, finden Sie hier eine Formel dafür:

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

4

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

Es ist nicht genau Basis 26, es gibt keine 0 im System. Wenn dies der Fall wäre, würde auf 'Z' 'BA' und nicht 'AA' folgen.


3

In Perl für eine Eingabe von 1 (A), 27 (AA) usw.

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

Verfeinern der ursprünglichen Lösung (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

Hier ist eine Actionscript-Version:

private var columnNumbers:Array = ['A', 'B', 'C', 'D', 'E', 'F' , 'G', 'H', 'I', 'J', 'K' ,'L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];

    private function getExcelColumnName(columnNumber:int) : String{
        var dividend:int = columnNumber;
        var columnName:String = "";
        var modulo:int;

        while (dividend > 0)
        {
            modulo = (dividend - 1) % 26;
            columnName = columnNumbers[modulo] + columnName;
            dividend = int((dividend - modulo) / 26);
        } 

        return columnName;
    }

2

JavaScript-Lösung

/**
 * 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
Versuchen Sie Index 26 und 27. Es ist sehr nah, aber um eins.
Eric

Wert = Math.floor (Wert / 26); sollte value = Math.ceil (value / 26) - 1 sein;
Henry Liu

2

Diese meine Codes zum Konvertieren einer bestimmten Nummer (Index beginnt bei 1) in eine Excel-Spalte.

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

Mein Unit Test:

    [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 für die Anzeige der maximalen Zellreferenz in Ihren Tests (XFD) - Sie würden nicht glauben, wie schwierig es ist, diese Informationen im Web zu finden.
Controlbox

2

Obwohl ich zu spät zum Spiel komme , ist Grahams Antwort alles andere als optimal. Insbesondere müssen Sie die Besetzung nicht moduloaufrufen, aufrufen ToString()und anwenden (int). In Anbetracht der Tatsache, dass Sie in der C # -Welt in den meisten Fällen mit der Nummerierung von 0 beginnen würden, ist hier meine Überarbeitung:

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

Ein anderer VBA-Weg

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

1

Hier ist meine super späte Implementierung in PHP. Dieser ist rekursiv. Ich habe es geschrieben, kurz bevor ich diesen Beitrag gefunden habe. Ich wollte sehen, ob andere dieses Problem bereits gelöst hatten ...

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

Ich versuche dasselbe in Java zu tun ... Ich habe folgenden Code geschrieben:

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

Sobald ich es mit columnNumber = 29 ausgeführt habe, erhalte ich das Ergebnis = "CA" (anstelle von "AC"). Gibt es Kommentare, was mir fehlt? Ich weiß, dass ich es mit StringBuilder umkehren kann ... Aber wenn ich mir die Antwort von Graham ansehe, bin ich ein wenig verwirrt ...


Graham sagt: columnName = Convert.ToChar(65 + modulo).ToString() + columnName(dh Wert + ColName). Hasan sagt: columnName += val;(dh ColName + Wert)
mcalex

Sie fügen den neuen Charakter hinzu, anstatt ihn vorzustellen. Sollte sein columnName = columnName + val.
Domenic D.

1

Coincise und elegante Ruby- Version:

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

Dies ist die Frage, zu der alle anderen sowie Google umleiten, also poste ich dies hier.

Viele dieser Antworten sind richtig, aber für einfache Situationen zu umständlich, z. B. wenn Sie nicht über 26 Spalten verfügen. Wenn Sie Zweifel haben, ob Sie in Doppelzeichenspalten gehen könnten, ignorieren Sie diese Antwort. Wenn Sie jedoch sicher sind, dass dies nicht der Fall ist, können Sie dies in C # so einfach tun:

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

Wenn Sie sich sicher sind, was Sie übergeben, können Sie sogar die Validierung entfernen und diese Inline verwenden:

(char)('A' + index)

Dies ist in vielen Sprachen sehr ähnlich, sodass Sie es nach Bedarf anpassen können.

Verwenden Sie diese Option nur, wenn Sie zu 100% sicher sind, dass Sie nicht mehr als 26 Spalten haben .


1

Danke für die Antworten hier !! hat mir geholfen, diese Hilfsfunktionen für die Interaktion mit der Google Sheets-API zu entwickeln, an der ich in Elixir / Phoenix arbeite

Hier ist, was ich mir ausgedacht habe (könnte wahrscheinlich eine zusätzliche Validierung und Fehlerbehandlung gebrauchen)

In 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

Und die Umkehrfunktion:

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

Und einige 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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.