如何在C#中将数字转换为Excel列名,而不使用自动化从Excel直接获取值。
Excel 2007的可能范围是1到16384,这是它支持的列数。转换后的值应该是Excel列名的形式,例如A、AA、AAA等。
如何在C#中将数字转换为Excel列名,而不使用自动化从Excel直接获取值。
Excel 2007的可能范围是1到16384,这是它支持的列数。转换后的值应该是Excel列名的形式,例如A、AA、AAA等。
以下是我的做法:
private string GetExcelColumnName(int columnNumber)
{
string columnName = "";
while (columnNumber > 0)
{
int modulo = (columnNumber - 1) % 26;
columnName = Convert.ToChar('A' + modulo) + columnName;
columnNumber = (columnNumber - modulo) / 26;
}
return columnName;
}
StringBuilder
进行尝试,但需要的时间大约是之前的两倍。这是一个非常短的字符串(最多3个字符 - Excel 2010可以到列XFD),因此最多只能进行2次字符串连接。(我使用了将整数1到16384(即Excel列A到XFD)翻译100次作为测试)。 - Grahammodulo
,调用 ToString()
,以及执行 (int)
强制转换。考虑到在大多数情况下,C# 中你会从 0 开始计数,以下是我的修改:<!-- language: c# -->
public static string GetColumnName(int index) // 以 0 为起点
{
const byte BASE = 'Z' - 'A' + 1;
var nameBuilder = new StringBuilder();while (index > 0)
{
index--;
nameBuilder.Insert(0, (char)('A' + index % BASE));
index /= BASE;
}
return nameBuilder.ToString();
} - Herman Kan如果有人需要在Excel中不使用VBA来完成此操作,这里有一个方法:
=SUBSTITUTE(ADDRESS(1;colNum;4);"1";"")
其中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
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))
你可能需要双向转换,例如从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;
}
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;
}
//旧帖
这是一个使用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;
}
这个答案是用 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;
}
我很惊讶到目前为止所有的解决方案都包含迭代或递归。
这是我的解决方案,它可以在常数时间内运行(没有循环)。此解决方案适用于所有可能的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);
}
}
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;
更新: Peter的评论是正确的。这就是在浏览器中编写代码的结果。:-) 我的解决方案没有编译,缺少最左边的字母,并且它以相反的顺序构建字符串-现在全部修复。
除了错误之外,算法基本上是将一个数字从10进制转换为26进制。
更新2: Joel Coehoorn 是对的-上面的代码将返回27的AB。如果它是真正的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;
使用递归轻松实现。
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) );
}
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;
}