如何将列号(例如127)转换为Excel列(例如AA)

558

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

Excel 2007的可能范围是1到16384,这是它支持的列数。转换后的值应该是Excel列名的形式,例如A、AA、AAA等。


2
不要忘记可用列数的限制。例如:* Excel 2003(v11)最多到IV,2^8或256列。* Excel 2007(v12)最多到XFD,2^14或16384列。 - Unsliced
1
可能是重复的问题:如何找到对应于给定整数的Excel列名? - Daniel Trebbien
这个问题被标记为C#和Excel。我将此问题标记为过时,因为我们生活在2016年,有EPPLUS。这是一个常用的C#库,可以在服务器上创建高级Excel电子表格。它是根据GNU图书馆通用公共许可证(LGPL)提供的。使用EPPlus,您可以轻松地获取列字符串。 - Tony_KiloPapaMikeGolf
请注意,行和列的限制更多取决于文件格式而不是 Excel 版本,并且对于每个工作簿可能会有所不同。如果将其保存为旧版或新版格式,即使是同一工作簿,它们也可能会发生变化。 - Slai
@Tony_KiloPapaMikeGolf 我认为这并不过时。事实上,EPPLUS已经更改了许可证,这可能不适合各种原因。另外,如果你所需的只是如此简单的东西,为什么要引入一个库呢?我正在使用OpenXML以Excel格式导出数据,只需要一些像这里提问的算法。为什么要混合使用库呢?需求因人而异。该问题并不过时,对于简单的用例仍然很相关。话虽如此,EPPLUS是一个非常酷的库。 ;) - MetalMikester
如果您需要用于Aspose Cells,它们有内置的帮助程序。请参考:https://docs.aspose.com/cells/net/names-and-indices/。 - Uwe Keim
60个回答

3

虽然我有些晚了,但是Graham的回答远非最佳解。特别是,您不必使用modulo,调用ToString()并应用(int)转换。考虑到在C#世界中,大多数情况下您将从0开始编号,这里是我的修改:

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

这似乎在循环体中具有与 Graham 的答案相同的两个数学运算。 - David Heffernan

3

在浏览了这里提供的所有版本后,我决定自己使用递归来实现。以下是我的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

1
这是错误的。27 => AB 而不是 AA,105 => DB 而不是 DA 等等。 - KekuSemau

3

已经有30多种解决方案了,但以下是我一行的C#解决方案...

public string IntToExcelColumn(int i)
{
    return ((i<16926? "" : ((char)((((i/26)-1)%26)+65)).ToString()) + (i<2730? "" : ((char)((((i/26)-1)%26)+65)).ToString()) + (i<26? "" : ((char)((((i/26)-1)%26)+65)).ToString()) + ((char)((i%26)+65)));
}

2
这是我编写的代码,用于将特定数字(索引从1开始)转换为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;
    }

我的单元测试:

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

在你的测试中展示最大单元格引用是多少(XFD),这样会得到加分,你不会相信在网上找到这个信息有多么困难。 - controlbox

2
抱歉,这是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 => AA
  • 列号:52 => AZ

2

这是一个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

另一种使用VBA的方法

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

2

优化原始解决方案(使用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

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
尝试使用索引26和27。非常接近,但是偏移了一个。 - Eric Rini
value = Math.floor(value / 26); 应改为 value = Math.ceil(value / 26) - 1; - Henry Liu

1
大多数之前的答案都是正确的。以下是将列号转换为Excel列的另一种方法。如果我们将其视为基础转换,则解决方案相当简单。只需将列号转换为基数26,因为只有26个字母。 以下是如何做到这一点:
步骤: - 将列设置为商 - 从商变量中减去1(从上一个步骤中),因为我们需要以97为a结束ascii table。 - 除以26并获得余数。 - 将+97添加到余数并转换为char(因为ASCII表中的97是“a”) - 商变为新商/ 26(因为我们可能超过26列) - 继续执行此操作,直到商大于0,然后返回结果
以下是执行此操作的代码 :)
def convert_num_to_column(column_num):
    result = ""
    quotient = column_num
    remainder = 0
    while (quotient >0):
        quotient = quotient -1
        remainder = quotient%26
        result = chr(int(remainder)+97)+result
        quotient = int(quotient/26)
    return result

print("--",convert_num_to_column(1).upper())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接