Jak przekonwertować numer kolumny (np. 127) na kolumnę Excela (np. AA)


474

Jak przekonwertować liczbę liczbową na nazwę kolumny Excel w C # bez korzystania z automatyzacji pobierania wartości bezpośrednio z Excela.

Excel 2007 ma możliwy zakres od 1 do 16384, czyli liczbę obsługiwanych kolumn. Otrzymane wartości powinny mieć postać nazw kolumn programu Excel, np. A, AA, AAA itp.


1
Nie zapominając, że istnieją ograniczenia w liczbie dostępnych kolumn. Np. * Excel 2003 (v11) idzie w górę do IV, 2 ^ 8 lub 256 kolumn). * Excel 2007 (wersja 12) idzie w górę do XFD, 2 ^ 14 lub 16384 kolumn.
Nieskompletowany


To pytanie jest oznaczone jako C # i wyróżnia się. Oznaczam to pytanie jako nieaktualne, ponieważ żyjemy w 2016 roku i jest EPPLUS . Często używana biblioteka C # do tworzenia zaawansowanych arkuszy kalkulacyjnych Excel na serwerze. Który jest udostępniany na podstawie: GNU Library General Public License (LGPL). Korzystając z EPPlus, możesz łatwo uzyskać ciąg kolumny.
Tony_KiloPapaMikeGolf

Należy pamiętać, że limity wierszy i kolumn zależą bardziej od formatu pliku niż wersji programu Excel i mogą być różne dla każdego skoroszytu. Można je zmienić nawet dla tego samego skoroszytu, jeśli jest zapisany w starszym lub nowszym formacie.
Slai

Odpowiedzi:


900

Oto jak to robię:

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
Ten kod działa dobrze. Zakłada się, że kolumna A to columnNumber 1. Musiałem dokonać szybkiej zmiany w moim systemie, używając kolumny A jako columnNumber 0. Zmieniłem wiersz int dividend na int dividend = columnNumber + 1; Keith
Keith Sirmons,

14
@Jduv właśnie wypróbował to za pomocą StringBuilder. Trwa to około dwa razy dłużej. Jest to bardzo krótki ciąg (maks. 3 znaki - Excel 2010 przechodzi do kolumny XFD), więc maksymalnie 2 ciągi łączące. (Użyłem 100 iteracji translacji liczb całkowitych 1 na 16384, tj. Kolumn Excela A na XFD, jako testu).
Graham

18
Dla lepszego zrozumienia zastąpiłbym 65
literą

11
Myślę, że lepiej byłoby użyć „A” zamiast 65. I 26 może być ocenione jako („Z” - „A” + 1), na przykład: const int AlphabetLength = „Z” - „A” + 1;
Denis Gladkiy,

5
Chociaż jestem spóźniony do gry, kod jest daleki od optymalności. W szczególności nie musisz używać obsady modulo, zadzwoń ToString()i zastosuj (int). Biorąc pod uwagę, że w większości przypadków w świecie C # zacząłbyś numerować od 0, oto moja wersja: <! - język: c # -> publiczny ciąg statyczny GetColumnName (indeks wewnętrzny) // bazujący na zerach {const byte BASE = 'Z „-„ A ”+ 1; nazwa ciągu = String.Empty; do {name = Convert.ToChar ('A' + index% BASE) + name; indeks = indeks / PODSTAWA - 1; } while (indeks> = 0); zwracane imię; }
Herman Kan,

63

Jeśli ktoś musi to zrobić w programie Excel bez VBA, oto sposób:

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

gdzie colNum jest numerem kolumny

A w 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
Przykład: = SUBSTITUTE (TEKST (ADRES (1,1000,4), „”), „1”, „”)
Dolph

Tak, używam Excela w lokalizacji, w której; służy do oddzielania argumentów funkcji w programie Excel. Dzięki za zwrócenie na to uwagi.
vzczc

2
Zrobiłem to bez funkcji TEKST. Do czego służy funkcja TEXT?
dayuloli

2
@dayuloli Już dawno napisano tę odpowiedź, masz rację, funkcja TEKST nie służy tutaj celowi. Zaktualizuje odpowiedź.
vzczc

21

Niestety, to jest Python zamiast C #, ale przynajmniej wyniki są poprawne:

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

Tak, przegłosowałem, nie publikuj Pythona, gdy pytanie brzmi C #. I tak, więcej osób powinno to robić.
KyloRen

1
Tytuł pytania nie oznacza C #, więc może przyjść tutaj wiele osób, które nie oczekują C #. Dlatego dziękuję za udostępnienie w Pythonie!
Tim-Erwin,

20

Może być konieczna konwersja w obie strony, np. Z adresu kolumny programu Excel, takiego jak AAZ, na liczbę całkowitą oraz z dowolnej liczby całkowitej do programu Excel. Dwie metody poniżej tego dokonają. Przyjmuje, że indeksowanie oparte jest na 1, pierwszym elementem w „tablicach” jest element nr 1. Brak ograniczeń rozmiaru, więc możesz używać adresów takich jak BŁĄD, a byłby to numer kolumny 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

Odkryłem błąd w moim pierwszym poście, więc postanowiłem usiąść i wykonać matematykę. Odkryłem, że system liczbowy używany do identyfikacji kolumn Excela nie jest systemem podstawowym 26, jak napisała inna osoba. Zastanów się, co następuje w podstawie 10. Możesz to również zrobić z literami alfabetu.

Spacja: ......................... S1, S2, S3: S1, S2, S3
............ ....................... 0, 00, 000: .. A, AA, AAA
............. ....................... 1, 01, 001: .. B, AB, AAB
.............. ......................…,…,…: ..…,…,…
............... ..................... 9, 99, 999: .. Z, ZZ, ZZZ
Suma stanów w przestrzeni: 10, 100, 1000: 26, 676, 17576
Ogółem państwa: ........... 1110 ............... 18278

Kolumny z liczbami Excela w poszczególnych przestrzeniach alfabetycznych przy użyciu podstawy 26. Widać, że ogólnie postęp przestrzeni stanów jest a, a ^ 2, a ^ 3,… dla pewnej podstawy a, a całkowita liczba stanów to ^ 2 + a ^ 3 +…

Załóżmy, że chcesz znaleźć całkowitą liczbę stanów A w pierwszych N odstępach. Wzór na to: A = (a) (a ^ N - 1) / (a-1). Jest to ważne, ponieważ musimy znaleźć przestrzeń N, która odpowiada naszemu indeksowi K. Jeśli chcę dowiedzieć się, gdzie K leży w systemie liczbowym, muszę zastąpić A literą K i rozwiązać dla N. Rozwiązanie to N = log { base a} (A (a-1) / a +1). Jeśli użyję przykładu a = 10 i K = 192, wiem, że N = 2.23804…. To mówi mi, że K leży na początku trzeciej przestrzeni, ponieważ jest nieco większa niż dwa.

Następnym krokiem jest ustalenie, jak daleko jesteśmy w obecnej przestrzeni. Aby to znaleźć, odejmij od K A wygenerowanego za pomocą podłogi N. W tym przykładzie podłoga N wynosi dwa. Tak więc A = (10) (10 ^ 2 - 1) / (10-1) = 110, jak można się spodziewać po połączeniu stanów dwóch pierwszych spacji. Należy to odjąć od K, ponieważ te pierwsze 110 stanów zostałoby już uwzględnione w pierwszych dwóch spacjach. Pozostaje nam 82 państwa. Tak więc w tym systemie liczb reprezentacja 192 w bazie 10 to 082.

Kod C # przy użyciu podstawowego indeksu wynosi 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;
    }

// Stary post

Rozwiązanie zerowe w języku 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 nie wiem dlaczego, ale podoba mi się to rozwiązanie. Nic wymyślnego, tylko dobre użycie logiki ... czytelny kod dla poziomów programisty. Jedną rzeczą, uważam, że najlepszą praktyką jest określenie pustego ciągu w C # jako ciąg znaków = ciąg.
Anonimowy typ

Tak, bardzo miłe wytłumaczenie. Ale czy mógłbyś również powiedzieć, że nie jest to również podstawa 27? Twoje wyjaśnienie pokazuje to podczas studiowania, ale szybka wzmianka na górze może zaoszczędzić trochę czasu innym osobom.
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;

Aktualizacja : Komentarz Petera jest słuszny. To właśnie otrzymuję za pisanie kodu w przeglądarce. :-) Moje rozwiązanie nie kompilowało się, brakowało mu lewej litery i budował ciąg w odwrotnej kolejności - wszystko teraz naprawione.

Pomijając błędy, algorytm zasadniczo konwertuje liczbę z bazy 10 na bazę 26.

Aktualizacja 2 : Joel Coehoorn ma rację - powyższy kod zwróci AB dla 27. Gdyby to była prawdziwa podstawowa liczba 26, AA byłaby równa A, a następna liczba po Z byłaby 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;

Kod nie zostanie skompilowany (nie zainicjowano sCol). Jeśli tak, to nie da właściwej odpowiedzi.
Peter,

5
ODPOWIEDŹ JEST NIEPRAWIDŁOWA. Baza 26 nie jest wystarczająco dobra. Zastanów się, co się stanie, gdy zawiniesz od Z do AA. Jeśli A jest równoważne cyfrze 0, to jest jak zawijanie od 9 do 00. Jeśli to 1 cyfra, to jak zawijanie od 9 do 11.
Joel Coehoorn

4
Nie jestem pewien po aktualizacji ... czy któryś z algorytmów jest teraz poprawny? A jeśli tak, to który, drugi?
Zredagowałbym

10

Łatwe z rekurencją.

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
.. lub pętla. Nie ma prawdziwego powodu, aby używać tutaj rekurencji.
Blorgbeard jest poza

1
To nie tylko baza 26, więc rozwiązanie rekurencyjne jest znacznie prostsze.
Joel Coehoorn

Ta procedura w rzeczywistości nie działa. Na przykład GetStandardExcelColumnName (26) zwraca @ GetStandardExcelColumnName (52) zwraca B @
sgmoore

1
najczystsze w przeciwieństwie do „najprostszego” rozwiązania jest najlepsze.
Anonimowy typ

9

Po prostu wrzucenie prostej dwuliniowej implementacji C # przy użyciu rekurencji, ponieważ wszystkie odpowiedzi tutaj wydają się znacznie bardziej skomplikowane niż to konieczne.

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

..I przekonwertowane na php:

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

Użyj ord('A')zamiast 65.
Mulli

8

Dziwi mnie, że wszystkie dotychczasowe rozwiązania zawierają iterację lub rekurencję.

Oto moje rozwiązanie, które działa w stałym czasie (bez pętli). To rozwiązanie działa dla wszystkich możliwych kolumn Excela i sprawdza, czy dane wejściowe można przekształcić w kolumnę Excela. Możliwe kolumny mieszczą się w zakresie [A, XFD] lub [1, 16384]. (Zależy to od twojej wersji Excela)

private static string Turn(uint col)
{
    if (col < 1 || col > 16384) //Excel columns are one-based (one = 'A')
        throw new ArgumentException("col must be >= 1 and <= 16384");

    if (col <= 26) //one character
        return ((char)(col + 'A' - 1)).ToString();

    else if (col <= 702) //two characters
    {
        char firstChar = (char)((int)((col - 1) / 26) + 'A' - 1);
        char secondChar = (char)(col % 26 + 'A' - 1);

        if (secondChar == '@') //Excel is one-based, but modulo operations are zero-based
            secondChar = 'Z'; //convert one-based to zero-based

        return string.Format("{0}{1}", firstChar, secondChar);
    }

    else //three characters
    {
        char firstChar = (char)((int)((col - 1) / 702) + 'A' - 1);
        char secondChar = (char)((col - 1) / 26 % 26 + 'A' - 1);
        char thirdChar = (char)(col % 26 + 'A' - 1);

        if (thirdChar == '@') //Excel is one-based, but modulo operations are zero-based
            thirdChar = 'Z'; //convert one-based to zero-based

        return string.Format("{0}{1}{2}", firstChar, secondChar, thirdChar);
    }
}

2
FYI: Odpowiedzi @ Grahama (i prawdopodobnie inne) są bardziej ogólne niż twoje: obsługują ponad 4 znaki w nazwach kolumn. I właśnie dlatego są iteracyjne.
Bernard Paul

W rzeczywistości, jeśli użyli nieograniczonej liczby całkowitej, a nie ints, wynikowa nazwa kolumny mogłaby być dowolnie długa (na przykład w przypadku odpowiedzi na python)
Bernard Paul

1
Jeśli moje dane są kiedykolwiek zbyt duże, aby pomieścić 16 384-kolumnowy arkusz kalkulacyjny, strzelę sobie w głowę. W każdym razie Excel nawet nie obsługuje wszystkich możliwych trzech literowych kolumn (odcina się na XFD, pomijając 1894 kolumny). W każdym razie teraz. W razie potrzeby zaktualizuję swoją odpowiedź.
user2023861,

:) nie wiedziałem o tym! Mój komentarz dotyczył teoretycznych właściwości różnych algorytmów.
Bernard Paul

To jest prawdopodobnie najprostsze i najczystsze rozwiązanie.
Juan

8

Ta odpowiedź jest w języku Java:

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

To samo wdrożenie w Javie

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

Po przejrzeniu wszystkich dostarczonych wersji tutaj, postanowiłem zrobić je sam, używając rekurencji.

Oto moja wersja 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

Trochę późno do gry, ale oto kod, którego używam (w 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
Zrobiłeś odwrotność tego, o co cię proszono, ale +1 dla twojego lambda-fu.
nurettin

IndexOfjest dość powolny, lepiej wstępnie wyrównaj odwrotne mapowanie.
Vlad

5

Chciałem dodać klasę statyczną, której używam, do interakcji między indeksem col a col Label. Używam zmodyfikowanej zaakceptowanej odpowiedzi dla mojej metody 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;
    }
}

Użyj tego jak ...

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

4

jeśli chcesz go tylko dla formuły komórkowej bez kodu, oto formuła dla niego:

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

4

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

Nie jest to dokładnie podstawa 26, w systemie nie ma 0. Jeśli tak, po „Z” następuje „BA”, a nie „AA”.


3

W perlu, dla wejścia 1 (A), 27 (AA) itp.

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

Udoskonalenie oryginalnego rozwiązania (w języku 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

Oto wersja 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

Rozwiązanie 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
Spróbuj indeks 26 i 27. Jest bardzo blisko, ale jeden po drugim.
Eric

wartość = Math.floor (wartość / 26); powinna być wartość = Math.ceil (wartość / 26) - 1;
Henry Liu,

2

Te moje kody do konwersji określonej liczby (indeks zaczynają się od 1) do kolumny 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;
    }

Mój test jednostkowy:

    [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 za pokazanie maksymalnego odwołania do komórki w twoich testach (XFD) - nie uwierzyłbyś, jak trudno jest znaleźć te informacje w Internecie.
skrzynka kontrolna

2

Chociaż jestem spóźniony do gry, odpowiedź Grahama daleka jest od optymizmu. W szczególności nie musisz używać obsady modulo, zadzwoń ToString()i zastosuj (int). Biorąc pod uwagę, że w większości przypadków w świecie C # zacząłbyś numerować od 0, oto moja wersja:

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

Kolejny sposób VBA

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

1

Oto moja bardzo późna implementacja w PHP. Ten jest rekurencyjny. Napisałem to tuż przed znalezieniem tego postu. Chciałem sprawdzić, czy inni już rozwiązali ten problem ...

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

Próbuję zrobić to samo w Javie ... Napisałem następujący kod:

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

Teraz, gdy uruchomię go z columnNumber = 29, daje mi wynik = „CA” (zamiast „AC”) jakieś komentarze, których mi brakuje? Wiem, że mogę odwrócić to przez StringBuilder .... Ale patrząc na odpowiedź Grahama, jestem trochę zdezorientowany ...


Graham mówi: columnName = Convert.ToChar(65 + modulo).ToString() + columnName(tj. Wartość + ColName). Hasan mówi: columnName += val;(tj. ColName + wartość)
mcalex

Dodajesz nową postać zamiast wcześniejszej. Powinno być columnName = columnName + val.
Domenic D.

1

Zgodna i elegancka wersja 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

To pytanie, na które przekierowują wszystkie inne osoby, a także Google, więc zamieszczam to tutaj.

Wiele z tych odpowiedzi jest poprawnych, ale zbyt skomplikowanych w prostych sytuacjach, na przykład gdy nie masz więcej niż 26 kolumn. Jeśli masz jakiekolwiek wątpliwości, czy możesz przejść do kolumn z podwójnymi znakami, zignoruj ​​tę odpowiedź, ale jeśli jesteś pewien, że nie, możesz to zrobić tak prosto jak w 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);
}

Cholera, jeśli masz pewność co do tego, co przekazujesz, możesz nawet usunąć weryfikację i użyć tego:

(char)('A' + index)

Będzie to bardzo podobne w wielu językach, więc możesz go dostosować w razie potrzeby.

Ponownie używaj tego tylko, jeśli masz 100% pewności, że nie będziesz miał więcej niż 26 kolumn .


1

Dzięki za odpowiedzi tutaj !! pomógł mi wymyślić te funkcje pomocnicze do interakcji z interfejsem API Arkuszy Google, nad którymi pracuję w Elixir / Phoenix

oto, co wymyśliłem (prawdopodobnie użyłbym dodatkowej weryfikacji i obsługi błędów)

W eliksir:

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

I funkcja odwrotna:

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

I niektóre testy:

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.