将数字转换为 Excel 的基数 26

Convert a number to Excel’s base 26

好的,我被一些看似简单的事情卡住了。我正在尝试将数字转换为基数 26(即 3 = C、27 = AA 等)。我猜我的问题与模型中没有 0 有关?不确定。但是如果你 运行 代码,你会看到数字 52、104,尤其是 676 左右的数字真的很奇怪。谁能给我一个关于我没有看到的提示?我会很感激的。 (为了避免浪费您的时间,@ 是 ascii char 64,A 是 ascii char 65)

def toBase26(x):
    x = int(x)
    if x == 0:
        return '0'
    if x < 0:
        negative = True
        x = abs(x)
    else:
        negative = False
    def digit_value (val):
        return str(chr(int(val)+64))
    digits = 1
    base26 = ""
    while 26**digits < x:
        digits += 1
    while digits != 0:
        remainder = x%(26**(digits-1))
        base26 += digit_value((x-remainder)/(26**(digits-1)))
        x = remainder
        digits -= 1
    if negative:
        return '-'+base26
    else:
        return base26

import io    
with io.open('numbers.txt','w') as f:
    for i in range(1000):
        f.write('{} is {}\n'.format(i,toBase26(i)))

因此,我通过对我的函数(while 循环中的 2 个 if 语句)进行一些更改找到了一个临时解决方法。无论如何,我的专栏被限制在 500 个以内,以下对函数的更改似乎可以解决 x = 676 的问题,所以我很满意。但是,如果你们中的任何人找到任何 x 的通用解决方案(可能是我的代码可能会有所帮助),那就太棒了!

def toBase26(x):
    x = int(x)
    if x == 0:
        return '0'
    if x < 0:
        negative = True
        x = abs(x)
    else:
        negative = False
    def digit_value (val):
        return str(chr(int(val)+64))
    digits = 1
    base26 = ""
    while 26**digits < x:
        digits += 1
    while digits != 0:
        remainder = x%(26**(digits-1))
        if remainder == 0:
            remainder += 26**(digits-1)
        if digits == 1:
            remainder -= 1
        base26 += digit_value((x-remainder)/(26**(digits-1)))
        x = remainder
        digits -= 1
    if negative:
        return '-'+base26
    else:
        return base26

转换为 Excel 的“base 26”时的问题是,对于 Excel,数字 ZZ 实际上是 26 * 26**1 + 26 * 26**0 = 702,而正常的 base 26 数字系统会从中得到 1 * 26**2 + 1 * 26**1 + 0 * 26**0 = 702 (BBA)。所以我们不能在这里使用通常的方法来转换这些数字。

相反,我们必须推出自己的 divmod_excel 函数:

def divmod_excel(n):
    a, b = divmod(n, 26)
    if b == 0:
        return a - 1, b + 26
    return a, b

有了它,我们可以创建一个 to_excel 函数:

import string
def to_excel(num):
    chars = []
    while num > 0:
        num, d = divmod_excel(num)
        chars.append(string.ascii_uppercase[d - 1])
    return ''.join(reversed(chars))

另外一个方向,这个比较简单

from functools import reduce
def from_excel(chars):
    return reduce(lambda r, x: r * 26 + x + 1, map(string.ascii_uppercase.index, chars), 0)

这组函数做对了:

>>> to_excel(26)
'Z'
>>> to_excel(27)
'AA'
>>> to_excel(702)
'ZZ'
>>> to_excel(703)
'AAA'
>>> from_excel('Z')
26
>>> from_excel('AA')
27
>>> from_excel('ZZ')
702
>>> from_excel('AAA')
703

我们实际上可以通过简单地检查我们是否可以将它们链接起来以重现原始数字来确认它们彼此正确地工作:

for i in range(100000):
    if from_excel(to_excel(i)) != i:
        print(i)
# (prints nothing)

抱歉,我是用 Pascal 写的,不知道 Python

function NumeralBase26Excel(numero: Integer): string;
var
  algarismo: Integer;
begin
  Result := '';
  numero := numero - 1;
  if numero >= 0 then
  begin
    algarismo := numero mod 26;
    if numero < 26 then
      Result := Chr(Ord('A') + algarismo)
    else
      Result := NumeralBase26Excel(numero div 26) + Chr(Ord('A') + algarismo);
  end;
end;

这是一个解决方案:

def get_xl_range_col_nr(rng):
    
    def xl_ord(char):
        return ord(char) - ord('@')

    return sum([xl_ord(value) * (26 ** key) 
                for key, value 
                in enumerate([car for car in rng.upper() if car not in '0123456789'][::-1])])

get_xl_range_col_nr('ABC456') # returns 731
get_xl_range_col_nr('C456')   # returns 3