如何在c#中将数值转换为Excel列名,而不使用直接从Excel中获取值的自动化。

Excel 2007的范围可能是1到16384,这是它支持的列数。结果值应以excel列名的形式出现,例如A、AA、AAA等。


当前回答

T-sql (sql server 18)

第一页的解决方案副本

CREATE FUNCTION dbo.getExcelColumnNameByOrdinal(@RowNum int)  
RETURNS varchar(5)   
AS   
BEGIN  
    DECLARE @dividend int = @RowNum;
    DECLARE @columnName varchar(max) = '';
    DECLARE @modulo int;

    WHILE (@dividend > 0)
    BEGIN  
        SELECT @modulo = ((@dividend - 1) % 26);
        SELECT @columnName = CHAR((65 + @modulo)) + @columnName;
        SELECT @dividend = CAST(((@dividend - @modulo) / 26) as int);
    END
    RETURN 
       @columnName;

END;

其他回答

如果你想以实用的方式引用单元格,那么如果你使用工作表的Cells方法,你会得到更可读的代码。它接受行和列索引,而不是传统的单元格引用。它与Offset方法非常相似。

..并转换为php:

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

虽然已经有了一堆有效的答案,但没有一个能深入到它背后的理论。

Excel列名是其数字的以26为基数的双射表示。这与普通的26进制有很大的不同(没有前导零),我真的建议阅读维基百科的条目来了解区别。例如,十进制值702(分解为26*26 + 26)以“普通”底数26 × 110表示(即1x26^2 + 1x26^1 + 0x26^0),以双射底数26 × ZZ表示(即26x26^1 + 26x26^0)。

除了区别之外,双射计数是一种位置符号,因此我们可以使用迭代(或递归)算法来执行转换,该算法在每次迭代中查找下一个位置的数字(类似于普通的基数转换算法)。

获得十进制数m的双射base-k表示的最后一个位置(索引为0的位置)的数字的一般公式是(f是天花板函数- 1):

m - (f(m / k) * k)

下一个位置的数字(即下标为1的数字)可以通过对f(m / k)的结果应用相同的公式来求得。我们知道,对于最后一位数字(即下标最高的数字),f(m / k)为0。

这构成了迭代的基础,该迭代查找十进制数的双射进制k中的每个连续数字。在伪代码中,它看起来像这样(digit()将一个十进制整数映射到它在双射进制中的表示——例如,digit(1)将在双射进制26中返回a):

fun conv(m)
    q = f(m / k)
    a = m - (q * k)
    if (q == 0)
        return digit(a)
    else
        return conv(q) + digit(a);

因此,我们可以将其转换为c# 2,以获得一个通用的“conversion to bijective base-k”ToBijective()例程:

class BijectiveNumeration {
    private int baseK;
    private Func<int, char> getDigit;
    public BijectiveNumeration(int baseK, Func<int, char> getDigit) {
        this.baseK = baseK;
        this.getDigit = getDigit;
    }

    public string ToBijective(double decimalValue) {
        double q = f(decimalValue / baseK);
        double a = decimalValue - (q * baseK);
        return ((q > 0) ? ToBijective(q) : "") + getDigit((int)a);
    }

    private static double f(double i) {
        return (Math.Ceiling(i) - 1);
    }
}

现在转换为双射base-26(我们的“Excel列名”用例):

static void Main(string[] args)
{
    BijectiveNumeration bijBase26 = new BijectiveNumeration(
        26,
        (value) => Convert.ToChar('A' + (value - 1))
    );

    Console.WriteLine(bijBase26.ToBijective(1));     // prints "A"
    Console.WriteLine(bijBase26.ToBijective(26));    // prints "Z"
    Console.WriteLine(bijBase26.ToBijective(27));    // prints "AA"
    Console.WriteLine(bijBase26.ToBijective(702));   // prints "ZZ"
    Console.WriteLine(bijBase26.ToBijective(16384)); // prints "XFD"
}

Excel的最大列索引是16384 / XFD,但是这段代码可以转换任何正数。

作为一个额外的奖励,我们现在可以很容易地转换为任何双射基。例如,以10为基数的双射:

static void Main(string[] args)
{
    BijectiveNumeration bijBase10 = new BijectiveNumeration(
        10,
        (value) => value < 10 ? Convert.ToChar('0'+value) : 'A'
    );

    Console.WriteLine(bijBase10.ToBijective(1));     // prints "1"
    Console.WriteLine(bijBase10.ToBijective(10));    // prints "A"
    Console.WriteLine(bijBase10.ToBijective(123));   // prints "123"
    Console.WriteLine(bijBase10.ToBijective(20));    // prints "1A"
    Console.WriteLine(bijBase10.ToBijective(100));   // prints "9A"
    Console.WriteLine(bijBase10.ToBijective(101));   // prints "A1"
    Console.WriteLine(bijBase10.ToBijective(2010));  // prints "19AA"
}

1这个一般的答案最终可以简化为其他正确的具体答案,但我发现,如果没有双射数背后的形式理论,很难完全掌握解决方案的逻辑。这也很好地证明了它的正确性。此外,还有几个类似的问题与此相关,有些与语言无关,有些则更通用。这就是为什么我认为这个答案的增加是有必要的,这个问题是一个很好的地方。

2 c#免责声明:我在c#中实现了一个例子,因为这是这里要求的,但我从未学习或使用过这种语言。我已经验证了它的编译和运行,但如果有必要,请调整它以适应语言的最佳实践/一般惯例。

这个例子只是为了正确和容易理解;它可以也应该优化性能(例如使用尾递归——但这似乎需要在c#中进行蹦蹦跳),并变得更安全(例如通过验证参数)。

抱歉,这是Python而不是c#,但至少结果是正确的:

def excel_column_number_to_name(column_number):
    output = ""
    index = column_number-1
    while index >= 0:
        character = chr((index%26)+ord('A'))
        output = output + character
        index = index/26 - 1

    return output[::-1]


for i in xrange(1, 1024):
    print "%4d : %s" % (i, excel_column_number_to_name(i))

通过这些测试用例:

列号:494286 => ABCDZ 列号:27 => 列号:52 => AZ

这是所有其他人以及谷歌重定向到的问题,所以我在这里发布这个。

这些答案中有许多是正确的,但对于简单的情况来说太麻烦了,比如当您的列不超过26个时。如果你怀疑你是否会进入双字符列,那么忽略这个答案,但如果你确定你不会,那么你可以在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);
}

见鬼,如果你对你传递的东西有信心,你甚至可以删除验证并使用内联:

(char)('A' + index)

这在许多语言中是非常相似的,因此您可以根据需要进行调整。

同样,只有在100%确定不超过26列的情况下才使用这种方法。