如何在C#中将数字转换为Excel列名,而不使用自动从Excel直接获取值.
Excel 2007的可能范围为1到16384,即它支持的列数.结果值应采用excel列名称的形式,例如A,AA,AAA等.
我是这样做的:
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; }
如果有人需要在没有VBA的情况下在Excel中执行此操作,请执行以下操作:
=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
对不起,这是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))
您可能需要双向转换,例如从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; }
我在第一篇文章中发现了一个错误,所以我决定坐下来做数学运算.我发现用于识别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; }
//老帖子
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; }
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;
更新:彼得的评论是正确的.这就是我在浏览器中编写代码所得到的.:-)我的解决方案没有编译,它错过了最左边的字母,它正在以相反的顺序构建字符串 - 所有现在都已修复.
除了错误之外,该算法基本上将数字从基数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;
简单的递归.
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) ); }
..并转换为PHP:
function GetExcelColumnName($columnNumber) { $columnName = ''; while ($columnNumber > 0) { $modulo = ($columnNumber - 1) % 26; $columnName = chr(65 + $modulo) . $columnName; $columnNumber = (int)(($columnNumber - $modulo) / 26); } return $columnName; }
这个答案是在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; }
只是使用递归抛出一个简单的两行C#实现,因为这里的所有答案似乎都比必要的复杂得多.
////// Gets the column letter(s) corresponding to the given column number. /// /// The one-based column index. Must be greater than zero. ///The desired column letter, or an empty string if the column number was invalid. public static string GetColumnLetter(int column) { if (column < 1) return String.Empty; return GetColumnLetter((column - 1) / 26) + (char)('A' + (column - 1) % 26); }
我很惊讶到目前为止所有解决方案都包含迭代或递归.
这是我在恒定时间内运行的解决方案(无循环).此解决方案适用于所有可能的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); } }
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; }
在这里查看了所有提供的版本后,我决定使用递归自己做一个.
这是我的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
游戏稍晚,但这是我使用的代码(在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)); }
我想抛出我使用的静态类,用于在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; } }
使用这个...
30.ColumnLabel(); // "AD" "AD".ColumnIndex(); // 30