자동화를 사용하여 Excel에서 직접 값을 가져 오지 않고 C #에서 숫자를 Excel 열 이름으로 변환하는 방법은 무엇입니까?
Excel 2007의 가능한 범위는 1-16384이며 지원되는 열 수입니다. 결과 값은 A, AA, AAA 등 엑셀 열 이름 형식이어야합니다.
자동화를 사용하여 Excel에서 직접 값을 가져 오지 않고 C #에서 숫자를 Excel 열 이름으로 변환하는 방법은 무엇입니까?
Excel 2007의 가능한 범위는 1-16384이며 지원되는 열 수입니다. 결과 값은 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;
}
StringBuilder
. 약 두 배의 시간이 걸립니다. 매우 짧은 문자열 (최대 3 자-Excel 2010은 XFD 열까지)이므로 최대 2 개의 문자열 연결입니다. (테스트로 정수 1에서 16384, 즉 Excel 열 A에서 XFD로 변환하는 100 회 반복 사용).
modulo
, 호출 ToString()
및 (int)
캐스트 를 사용할 필요는 없습니다 . C # 세계에서 대부분의 경우 0부터 번호 매기기를 시작한다는 것을 고려할 때 여기에 내 개정이 있습니다 : <!-language : c #-> public static string GetColumnName (int index) // 0부터 시작하는 {const byte BASE = 'Z '-'A '+ 1; 문자열 이름 = String.Empty; do {name = Convert.ToChar ( 'A'+ index % BASE) + 이름; 인덱스 = 인덱스 / BASE-1; } while (인덱스> = 0); 반환 이름; }
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
죄송합니다. C # 대신 Python이지만 최소한 결과는 정확합니다.
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))
AAZ와 같은 Excel 열 주소에서 정수로, 정수에서 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은 base 26을 사용하여 개별 알파벳 공간의 열에 번호를 매 깁니다. 일반적으로 상태 공간 진행은 a, a ^ 2, a ^ 3,… 일부 a의 경우 총 상태 수는 + a입니다. ^ 2 + a ^ 3 +….
첫 번째 N 공간에서 총 상태 수 A를 찾고 싶다고 가정하십시오. 그렇게하는 공식은 A = (a) (a ^ N-1) / (a-1)입니다. 인덱스 K에 해당하는 공간 N을 찾아야하기 때문에 중요합니다. 숫자 시스템에서 K가 어디에 있는지 알아 내려면 A를 K로 바꾸고 N을 풀어야합니다. 해결책은 N = log { 기본 a} (A (a-1) / a +1). a = 10 및 K = 192의 예를 사용하면 N = 2.23804… 이것은 K가 두 번째보다 약간 크기 때문에 세 번째 공간의 시작 부분에 있다는 것을 말해줍니다.
다음 단계는 현재 공간에서 얼마나 멀리 있는지 정확히 찾는 것입니다. 이를 찾으려면 N의 바닥을 사용하여 생성 된 A를 K에서 빼십시오.이 예에서 N의 바닥은 2입니다. 따라서 처음 두 공백의 상태를 결합 할 때 예상되는대로 A = (10) (10 ^ 2 – 1) / (10-1) = 110입니다. 처음 110 개 주가 처음 두 공간에서 이미 설명 되었기 때문에 K에서 빼야합니다. 이로 인해 82 개 주가 생깁니다. 따라서이 수 체계에서 밑 10의 192는 082입니다.
기본 인덱스 0을 사용하는 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;
업데이트 : 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) );
}
여기에있는 모든 대답이 필요한 것보다 훨씬 복잡해 보이기 때문에 재귀를 사용하여 간단한 두 줄의 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);
}
.. 그리고 PHP로 변환 :
function GetExcelColumnName($columnNumber) {
$columnName = '';
while ($columnNumber > 0) {
$modulo = ($columnNumber - 1) % 26;
$columnName = chr(65 + $modulo) . $columnName;
$columnNumber = (int)(($columnNumber - $modulo) / 26);
}
return $columnName;
}
ord('A')
65 대신 사용하십시오 .
지금까지의 모든 솔루션에 반복 또는 재귀가 포함되어 놀랐습니다.
일정한 시간 (루프 없음)으로 실행되는 솔루션이 있습니다. 이 솔루션은 가능한 모든 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
s가 아닌 경우, 결과로 나오는 열 이름은 임의로 길어질 수 있습니다 (예를 들어, 파이썬 답변의 경우)
이 답변은 자바 스크립트입니다.
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;
}
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;
}
게임에 약간 늦었지만 여기에 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));
}
IndexOf
매우 느리다면 리버스 매핑을 미리 계산하는 것이 좋습니다.
col 인덱스와 col Label 간의 상호 작용을 위해 사용하는 정적 클래스를 던지고 싶었습니다. 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
델파이 (파스칼)에서 :
function GetExcelColumnName(columnNumber: integer): string;
var
dividend, modulo: integer;
begin
Result := '';
dividend := columnNumber;
while dividend > 0 do begin
modulo := (dividend - 1) mod 26;
Result := Chr(65 + modulo) + Result;
dividend := (dividend - modulo) div 26;
end;
end;
원래 솔루션을 다듬기 (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;
}
}
액션 스크립트 버전은 다음과 같습니다.
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;
}
자바 스크립트 솔루션
/**
* 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)를 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));
}
비록 게임에 늦었지만 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;
}
다음은 PHP에서의 초고속 구현입니다. 이것은 재귀 적입니다. 이 게시물을 찾기 직전에 썼습니다. 다른 사람들 이이 문제를 이미 해결했는지 확인하고 싶었습니다 ...
public function GetColumn($intNumber, $strCol = null) {
if ($intNumber > 0) {
$intRem = ($intNumber - 1) % 26;
$strCol = $this->GetColumn(intval(($intNumber - $intRem) / 26), sprintf('%s%s', chr(65 + $intRem), $strCol));
}
return $strCol;
}
Java에서 동일한 작업을 수행하려고합니다 ... 다음 코드를 작성했습니다.
private String getExcelColumnName(int columnNumber) {
int dividend = columnNumber;
String columnName = "";
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
char val = Character.valueOf((char)(65 + modulo));
columnName += val;
dividend = (int)((dividend - modulo) / 26);
}
return columnName;
}
이제 columnNumber = 29로 실행하면 누락 된 내용에 대한 결과 = "CA"( "AC"대신)가 표시됩니까? StringBuilder로 되돌릴 수 있다는 것을 알고 있습니다 .... Graham의 답변을 보면 조금 혼란 스럽습니다 ....
columnName = Convert.ToChar(65 + modulo).ToString() + columnName
(가치 + ColName). 하산의 말 : columnName += val;
(즉 ColName + value)
columnName = columnName + val
합니다.
이것은 Google뿐만 아니라 다른 모든 사람들에게 질문으로 여기에 게시하고 있습니다.
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)
이것은 많은 언어에서 매우 유사하므로 필요에 따라 조정할 수 있습니다.
다시 말하지만, 열이 26 개를 초과하지 않을 것이라고 100 % 확신하는 경우에만 사용하십시오 .
답변을 주셔서 감사합니다! Elixir / Phoenix에서 작업중인 Google Sheets API와의 상호 작용을 위해 이러한 도우미 기능을 생각해 냈습니다.
여기에 내가 생각해 낸 것이 있습니다 (아마도 추가 유효성 검사 및 오류 처리를 사용할 수 있음)
엘릭서 :
def number_to_column(number) do
cond do
(number > 0 && number <= 26) ->
to_string([(number + 64)])
(number > 26) ->
div_col = number_to_column(div(number - 1, 26))
remainder = rem(number, 26)
rem_col = cond do
(remainder == 0) ->
number_to_column(26)
true ->
number_to_column(remainder)
end
div_col <> rem_col
true ->
""
end
end
그리고 역함수 :
def column_to_number(column) do
column
|> to_charlist
|> Enum.reverse
|> Enum.with_index
|> Enum.reduce(0, fn({char, idx}, acc) ->
((char - 64) * :math.pow(26,idx)) + acc
end)
|> round
end
그리고 몇 가지 테스트 :
describe "test excel functions" do
@excelTestData [{"A", 1}, {"Z",26}, {"AA", 27}, {"AB", 28}, {"AZ", 52},{"BA", 53}, {"AAA", 703}]
test "column to number" do
Enum.each(@excelTestData, fn({input, expected_result}) ->
actual_result = BulkOnboardingController.column_to_number(input)
assert actual_result == expected_result
end)
end
test "number to column" do
Enum.each(@excelTestData, fn({expected_result, input}) ->
actual_result = BulkOnboardingController.number_to_column(input)
assert actual_result == expected_result
end)
end
end