Convert an excel or spreadsheet column letter to its number in Pythonic fashion

前端 未结 17 1193
夕颜
夕颜 2020-12-09 04:18

Is there a more pythonic way of converting excel-style columns to numbers (starting with 1)?

Working code up to two letters:



        
相关标签:
17条回答
  • 2020-12-09 05:00

    I made this one-liner:

    colNameToNum = lambda cn: sum([((ord(cn[-1-pos]) - 64) * 26 ** pos) for pos in range(len(cn))])
    

    It works by iterating through the letters in reverse order and multiplying by 1, 26, 26 * 26 etc, then summing the list. This method would be compatible with longer strings of letters, too.

    I call it with:

    print(colNameToNum("AA")) # 27

    or

    print(colNameToNum("XFD")) # the highest column allowed, I believe. Result = 16384

    0 讨论(0)
  • 2020-12-09 05:00

    Use:

    LETTERS = list(string.ascii_uppercase)
    def column_number(column_id):
        return sum([(LETTERS.index(j)+1)*(26**i) for i,j in enumerate(column_id[::-1])])
    

    There are several parts to this one-liner, so here's the explanation:

    column_id[::-1]: reverses the string, e.g. converts 'AZ' to 'ZA', there's a good reason to do so, which we will see in a bit.

    enumerate(): produces a iterable, e.g. (0, 'Z'), (1, 'A')

    With some observation:

     A -> 1  = (26**0)*1              # ** is the exponential operator
     B -> 2  = (26**0)*2 
     Z -> 26 = (26**0)*26
    AA -> 27 = (26**0)*1  + (26**1)*1
    AB -> 28 = (26**0)*2  + (26**1)*1
    AZ -> 52 = (26**0)*26 + (26**1)*1  # recall that we have (0, 'Z'), (1, 'A')
    

    Reversing the column_id and enumerate() allows us to use the index as the exponent for 26. The rest is now trivial.

    LETTERS.index(j): gives us the index of the letter in LETTERS

    sum(): takes a list of numbers and returns the total.

    0 讨论(0)
  • 2020-12-09 05:05

    One-liners tested in Python 2.7.1 and 3.5.2

    excel_col_num = lambda a: 0 if a == '' else 1 + ord(a[-1]) - ord('A') + 26 * excel_col_num(a[:-1])
    
    excel_col_name = lambda n: '' if n <= 0 else excel_col_name((n - 1) // 26) + chr((n - 1) % 26 + ord('A'))
    

    Multi-liners likewise

    def excel_column_name(n):
        """Number to Excel-style column name, e.g., 1 = A, 26 = Z, 27 = AA, 703 = AAA."""
        name = ''
        while n > 0:
            n, r = divmod (n - 1, 26)
            name = chr(r + ord('A')) + name
        return name
    
    def excel_column_number(name):
        """Excel-style column name to number, e.g., A = 1, Z = 26, AA = 27, AAA = 703."""
        n = 0
        for c in name:
            n = n * 26 + 1 + ord(c) - ord('A')
        return n
    
    def test (name, number):
        for n in [0, 1, 2, 3, 24, 25, 26, 27, 702, 703, 704, 2708874, 1110829947]:
            a = name(n)
            n2 = number(a)
            a2 = name(n2)
            print ("%10d  %-9s  %s" % (n, a, "ok" if a == a2 and n == n2 else "error %d %s" % (n2, a2)))
    
    test (excel_column_name, excel_column_number)
    test (excel_col_name, excel_col_num)
    

    All tests print

             0             ok
             1  A          ok
             2  B          ok
             3  C          ok
            24  X          ok
            25  Y          ok
            26  Z          ok
            27  AA         ok
           702  ZZ         ok
           703  AAA        ok
           704  AAB        ok
       2708874  EXCEL      ok
    1110829947  COLUMNS    ok
    
    0 讨论(0)
  • 2020-12-09 05:05

    Here is one way to do it. It is a variation on code in the XlsxWriter module:

    def col_to_num(col_str):
        """ Convert base26 column string to number. """
        expn = 0
        col_num = 0
        for char in reversed(col_str):
            col_num += (ord(char) - ord('A') + 1) * (26 ** expn)
            expn += 1
    
        return col_num
    
    
    >>> col_to_num('A')
    1
    >>> col_to_num('AB')
    28
    >>> col_to_num('ABA')
    729
    >>> col_to_num('AAB')
    704
    
    0 讨论(0)
  • 2020-12-09 05:06

    Here is a recursive solution:

    def column_string_to_num(s):
        n = ord(s[-1]) - 64
        if s[:-1]:
            return 26 * (column_string_to_num(s[:-1])) + n
        else:
            return n
        
    column_string_to_num("AB")
    #output: 28
    

    The inverse can also be defined recursively, in a similar way:

    def column_num_to_string(n):
        n, rem = divmod(n - 1, 26)
        next_char = chr(65 + rem)
        if n:
            return column_string(n) + next_char
        else:
            return next_char
    
    column_num_to_string(28)
    #output: 'AB'
    
    0 讨论(0)
提交回复
热议问题