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

rob*_*oll 452 c# excel

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

Excel 2007的可能范围为1到16384,即它支持的列数.结果值应采用excel列名称的形式,例如A,AA,AAA等.

Gra*_*ham 870

我是这样做的:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这段代码效果很好.它假设A列是columnNumber 1.我必须使用A列作为columnNumber 0快速更改我的系统.我将int dividend更改为int dividend = columnNumber + 1; 基思 (62认同)
  • 为了更好地理解,我会用'A'代替65 (15认同)
  • @Jduv只是用`StringBuilder`试了一下.它需要大约两倍的时间.这是一个非常短的字符串(最多3个字符 - Excel 2010上升到XFD列),因此最多2个字符串连接.(我使用100次迭代翻译整数1到16384,即Excel列A到XFD,作为测试). (14认同)
  • 我认为最好使用'A'而不是65.而26可以被评估为('Z' - 'A'+ 1),例如:const int AlphabetLength ='Z' - 'A'+ 1; (10认同)
  • 虽然我迟到了游戏,但代码远非最佳.特别是,你不必使用`modulo`,调用`ToString()`并应用`(int)`cast.考虑到在大多数情况下在C#世界中你将从0开始编号,这是我的修订版:<! - language:c# - > public static string GetColumnName(int index)//从零开始{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); 返回名称; } (3认同)

vzc*_*czc 62

如果有人需要在没有VBA的情况下在Excel中执行此操作,请执行以下操作:

=SUBSTITUTE(ADDRESS(1;colNum;4);"1";"")
Run Code Online (Sandbox Code Playgroud)

其中colNum是列号

在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
Run Code Online (Sandbox Code Playgroud)

  • 示例:= SUBSTITUTE(TEXT(ADDRESS(1,1000,4),""),"1","") (2认同)
  • 我这样做是没有TEXT函数的。TEXT函数的作用是什么? (2认同)
  • @dayuloli自从写了这个答案很长时间以来,您是对的,TEXT函数在这里不起作用。将更新答案。 (2认同)

小智 19

对不起,这是Python而不是C#,但至少结果是正确的:

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))
Run Code Online (Sandbox Code Playgroud)

  • 问题的标题并不暗示C#,所以很多人来到这里可能并不期待C#。因此,感谢您在 Python 中的分享! (2认同)

小智 17

您可能需要双向转换,例如从Excel列地址(如AAZ)到整数以及从任何整数到Excel.下面的两种方法就是这样做的.假设基于1的索引,"数组"中的第一个元素是元素编号1.此处没有大小限制,因此您可以使用ERROR之类的地址,这将是列号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;
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ohn 13

我在第一篇文章中发现了一个错误,所以我决定坐下来做数学运算.我发现用于识别Excel列的数字系统不是另一个人发布的基础26系统.请考虑基数10中的以下内容.您也可以使用字母表中的字母执行此操作.

空间:......................... S1,S2,S3:S1,S2,S3
............ ........................ 0,00,000:A,AA,AAA
............. ....................... 1,01,001:.. B,AB,AAB
.............. ...................... ...,...,......:......,......,......
............... .....................
9,99,999 :.. Z,ZZ,ZZZ 空间总状态:10,100,1000:26,676,17576
总国家:............... 1110 ................ 18278

Excel使用基数26对各个字母空间中的列进行编号.您可以看到,通常情况下,状态空间进展为a,a ^ 2,a ^ 3,...对于某个基数a,状态总数为a + a ^ 2 + a ^ 3 + ....

假设您要查找前N个空格中的状态A的总数.这样做的公式是A =(a)(a ^ N-1)/(a-1).这很重要,因为我们需要找到与我们的索引K相对应的空间N.如果我想找出K在数字系统中的位置,我需要用K替换A并求解N.解是N = log {基础a}(A(a-1)/ a +1).如果我使用a = 10和K = 192的例子,我知道N = 2.23804 .... 这告诉我K位于第三个空间的开头,因为它大于两个.

下一步是准确找到我们当前空间的距离.为了找到这个,从K中减去使用N的层数生成的A.在该示例中,N的层数是2.因此,A =(10)(10 ^ 2 - 1)/(10-1)= 110,正如您组合前两个空格的状态时所预期的那样.这需要从K中减去,因为在前两个空间中已经考虑了前110个状态.这让我们有82个州.因此,在该数字系统中,基数10中的192的表示是082.

使用基本索引为零的C#代码是

    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;
    }
Run Code Online (Sandbox Code Playgroud)

//老帖子

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;
    }
Run Code Online (Sandbox Code Playgroud)


des*_*eim 12

尽管已经有很多有效的答案1,但没有一个深入探讨其背后的理论。

Excel 列名称是其数字的双射基数 26表示形式。这与普通的 26 进制(没有前导零)有很大不同,我强烈建议您阅读Wikipedia 条目以掌握差异。例如,十进制值702(在 中分解)在“普通”基数 26 中由(ie )26*26 + 26表示,在双射基数 26 中由(ie ) 表示。1101x26^2 + 1x26^1 + 0x26^0ZZ26x26^1 + 26x26^0

抛开差异不谈,双射计数一种位置表示法,因此我们可以使用迭代(或递归)算法执行转换,该算法在每次迭代中找到下一个位置的数字(类似于普通的基本转换算法)。

获取十进制数k的双射基数表示的最后一个位置(索引为 0 的位置)的数字的通用公式m是(f上限函数减 1):

m - (f(m / k) * k)
Run Code Online (Sandbox Code Playgroud)

通过对 的结果应用相同的公式可以找到下一个位置的数字(即索引为 1 的数字)f(m / k)。我们知道最后一位数字(即索引最高的数字)f(m / k)是 0。

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

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

因此,我们可以将其转换为 C# 2,以获得通用的3 “转换为双射基 k”ToBijective()例程:

m - (f(m / k) * k)
Run Code Online (Sandbox Code Playgroud)

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

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

Excel 的最大列索引是16384/ XFD,但此代码将转换任何正数。

作为额外的好处,我们现在可以轻松转换为任何双射基。例如对于双射以 10 为底

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

2 C# 免责声明:我用 C# 实现了一个示例,因为这是这里所要求的,但我从未学习或使用过该语言。我已经验证它确实可以编译和运行,但如有必要,请对其进行调整以适应语言最佳实践/一般约定。

3本示例仅旨在正确且易于理解;它可以而且应该优化性能问题(例如使用尾递归——但这似乎需要C# 中的蹦床),并使其更安全(例如通过验证参数)。


Fra*_*nov 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;
Run Code Online (Sandbox Code Playgroud)

更新:彼得的评论是正确的.这就是我在浏览器中编写代码所得到的.:-)我的解决方案没有编译,它错过了最左边的字母,它正在以相反的顺序构建字符串 - 所有现在都已修复.

除了错误之外,该算法基本上将数字从基数10转换为基数26.

更新2:Joel Coehoorn是对的 - 上面的代码将返回AB为27.如果它是真正的基数26,AA将等于A,Z之后的下一个数字将是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;
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是错的.Base26还不够好.想想当你从Z换成AA时会发生什么.如果A相当于0位数,那就像是从9到00进行包装.如果它是1位数,就像从9到11包装一样. (5认同)
  • 更新后我不清楚...现在哪个算法正确?如果是这样,哪一个,第二个?我编辑这个并为后人做出明显的贡献.... (4认同)

Pet*_*ter 10

简单的递归.

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) );
}
Run Code Online (Sandbox Code Playgroud)

  • ..或循环。这里没有真正的理由使用递归。 (2认同)
  • 它不仅仅是基数 26,因此递归解决方案要简单得多。 (2认同)
  • 与“最简单”的解决方案相反,最清晰的解决方案是最好的。 (2认同)

Ste*_*hry 8

..并转换为PHP:

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


MaV*_*SCy 8

这个答案是在javaScript中:

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;
}
Run Code Online (Sandbox Code Playgroud)


Ext*_*rey 8

只是使用递归抛出一个简单的两行C#实现,因为这里的所有答案似乎都比必要的复杂得多.

/// <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);
}
Run Code Online (Sandbox Code Playgroud)


use*_*861 7

我很惊讶到目前为止所有解决方案都包含迭代或递归.

这是我在恒定时间内运行的解决方案(无循环).此解决方案适用于所有可能的Excel列,并检查输入是否可以转换为Excel列.可能的列在[A,XFD]或[1,16384]范围内.(这取决于您的Excel版本)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考:@ Graham的答案(可能还有其他答案)比你的更通用:它们在列名中支持4个以上的字符.而这恰恰是他们迭代的原因. (2认同)
  • 如果我的数据对于 16,384 列的电子表格来说太大,我就会朝自己的头开枪。无论如何,Excel 甚至不支持所有可能的三字母列(它在 XFD 处截断,留下 1,894 列)。无论如何现在。以后我会根据需要更新我的答案。 (2认同)

Bal*_*N.S 6

Java中的相同实现

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; 
    }  
Run Code Online (Sandbox Code Playgroud)


use*_*708 5

在这里查看了所有提供的版本后,我决定使用递归自己做一个.

这是我的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
Run Code Online (Sandbox Code Playgroud)


Kel*_*y L 5

游戏稍晚,但这是我使用的代码(在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));
}
Run Code Online (Sandbox Code Playgroud)

  • 你做了相反的问题,但你的lambda-fu是+1. (2认同)

t3d*_*son 5

我想抛出我使用的静态类,用于在col索引和col标签之间进行插入.我为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;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这个...

30.ColumnLabel(); // "AD"
"AD".ColumnIndex(); // 30
Run Code Online (Sandbox Code Playgroud)