Как преобразовать числовое число в имя столбца Excel на C# без использования автоматизации, получая значение непосредственно из Excel.
Excel 2007 имеет возможный диапазон от 1 до 16384, т.е. количество поддерживаемых столбцов. Результирующие значения должны быть в форме имен столбцов Excel, например A, AA, AAA и т. д.
возможный дубликат Как найти имя столбца Excel, соответствующее заданному целому числу?
Этот вопрос помечен как C# и Excel. Я помечаю этот вопрос как устаревший, потому что мы живем в 2016 году и есть EPPLUS. Часто используемая библиотека C# для создания расширенных электронных таблиц Excel на сервере. Это доступно по лицензии GNU Library General Public License (LGPL). Используя EPPlus, вы можете легко получить строку столбца.
Обратите внимание, что ограничения строк и столбцов больше зависят от формата файла, чем от версии Excel, и могут быть разными для каждой книги. Они могут измениться даже для одной и той же книги, если она сохранена в более старом или новом формате.
@Tony_KiloPapaMikeGolf Я не думаю, что это устарело. Фактически, EPPLUS изменили свое лицензирование, которое может не подходить всем по разным причинам. Кроме того, зачем приносить библиотеку, если все, что вам нужно, настолько простое? Я экспортирую данные в формате Excel с помощью OpenXML, и мне понадобилось всего несколько алгоритмов, подобных тому, о котором здесь спрашивают. Зачем добавлять в микс библиотеку? Потребности меняются. Вопрос не устарел и актуален для простых случаев использования. Тем не менее, EPPLUS - довольно классная библиотека. ;)
Если вам это нужно для ячеек Aspose, у них есть встроенные помощники.





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: Джоэл Кохорн верен - приведенный выше код вернет 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;
Код не компилируется (sCol не инициализирован). Если это так, он не даст правильного ответа.
ЭТОТ ОТВЕТ НЕПРАВИЛЬНЫЙ. Base26 недостаточно хорош. Подумайте, что произойдет, если вы перейдете от Z к AA. Если A эквивалентно цифре 0, то это похоже на перенос от 9 до 00. Если это цифры 1, это похоже на перенос от 9 до 11.
Я не понимаю после обновлений ... правильный ли какой-либо из алгоритмов? И если да, то какой, второй? Я бы отредактировал это и сделаю очевидным для потомков ...
Легко с рекурсией.
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) );
}
.. или петлю. Здесь нет реальной причины использовать рекурсию.
Это не просто база 26, поэтому рекурсивное решение намного проще.
Эта процедура на самом деле не работает. Например, GetStandardExcelColumnName (26) возвращает @ GetStandardExcelColumnName (52) возвращает B @
самое ясное, а не «простое» решение - лучшее.
Извините, это 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))
Да, я проголосовал против, не публикуйте Python, когда вопрос касается C#. И да, больше людей должны это делать.
Название вопроса не подразумевает C#, поэтому сюда может прийти много людей, которые не исключают C#. Следовательно, спасибо за то, что поделились Python!
Вот как я это делаю:
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;
}
Этот код работает хорошо. Предполагается, что столбец A - это номер столбца 1. Мне пришлось быстро внести изменения, чтобы учесть мою систему, используя столбец А как номер столбца 0. Я изменил строку int dividnd на int dividnd = columnNumber + 1; Кит
Вероятно, вы могли бы сделать это немного быстрее, удалив конкатенации строк и используя StringBuilder. Я работаю с OpenXML, поэтому мне нужна максимальная скорость. +1 хотя, это правильно.
@Jduv только что попробовал использовать StringBuilder. На это уходит примерно вдвое больше времени. Это очень короткая строка (максимум 3 символа - Excel 2010 идет до столбца XFD), поэтому не более двух конкатенаций строк. (В качестве теста я использовал 100 итераций перевода целых чисел от 1 до 16384, то есть столбцов A Excel в XFD).
Для лучшего понимания я бы заменил 65 на «А».
Собираюсь написать это сам, рад, что проверил, это более элегантно, чем то, о чем я думал.
Я думаю, было бы лучше использовать «A» вместо 65. И 26 можно было бы оценить как («Z» - «A» + 1), например: const int AlphabetLength = «Z» - «A» + 1;
@Graham Я только что попробовал StringBuilder - он всего на ~ 1,2 медленнее, чем конкатенация строк (не в 2 раза). Но все равно медленнее струны.
Почему вы ЕЩЕ ЕЩЕ используете ДРУГОЙ переменный (дивиденд), выделенный в стеке? Используйте сам параметр (columnNumber) ... Это не ссылка и не указатель, поэтому его совершенно безопасно использовать - и он работает так же быстро, как и еще одна локальная переменная. PS: параметры - это локальные переменные ...
Хотя я опаздываю в игру, код далек от оптимального, в частности, вам не нужно использовать modulo, вызывать ToString() и применять приведение (int). Учитывая, что в большинстве случаев в мире C# вы начинаете нумерацию с 0, вот моя редакция: <! - language: C# -> public static string GetColumnName (int index) // с нуля {const byte BASE = 'Z '-' А '+ 1; имя строки = String.Empty; сделать {имя = Convert.ToChar ('A' + индекс% BASE) + имя; index = index / BASE - 1; } while (индекс> = 0); возвращаемое имя; }
@HermanKan, как мне изменить его, чтобы он не начинался с нуля. Столбцы Excel не отсчитываются от нуля, они начинаются с 1. В противном случае это бесполезно.
@LeoGurdian, это довольно просто: ... GetColumnName(int col) { int index = col - 1; ...
Не знаю, как он был выбран как правильный ответ. Поскольку для меня это не подходит для базового тестового примера: он дает AZ, когда я прохожу 26. Правильный ответ - Z. Пожалуйста, поправьте меня, если я что-то упускаю.
Привет @ Грэм, не так ли? Я скопировал этот фрагмент и выполнил его без каких-либо изменений в C#, но я не получил Z в качестве ответа, когда ввел 26. Это как-то связано с языком?
Привет @bill, я снова забыл ответить. Показанный здесь фрагмент действительно сработал для меня. Я допустил ошибку при копировании. Сообщите мне, если у вас возникнут проблемы.
Понятно. Кажется, что Dividnd = (int) ((Dividend - modulo-1) / 26) тоже подойдет?
@HermanKan: Надеюсь, вы провели несколько тестов производительности, которые ясно показывают, что modulo неоптимален по сравнению с .ToString()? XD
private String getColumn(int c) {
String s = "";
do {
s = (char)('A' + (c % 26)) + s;
c /= 26;
} while (c-- > 0);
return s;
}
Это не совсем база 26, в системе нет 0. Если бы это было так, за «Z» следовало бы «BA», а не «AA».
Я использую его в VB.NET 2003, и он хорошо работает ...
Private Function GetExcelColumnName(ByVal aiColNumber As Integer) As String
Dim BaseValue As Integer = Convert.ToInt32(("A").Chars(0)) - 1
Dim lsReturn As String = String.Empty
If (aiColNumber > 26) Then
lsReturn = GetExcelColumnName(Convert.ToInt32((Format(aiColNumber / 26, "0.0").Split("."))(0)))
End If
GetExcelColumnName = lsReturn + Convert.ToChar(BaseValue + (aiColNumber Mod 26))
End Function
Если вы хотите ссылаться на ячейку программно, вы получите гораздо более читаемый код, если будете использовать метод Cells листа. Он принимает индекс строки и столбца вместо традиционной ссылки на ячейку. Он очень похож на метод смещения.
? Ячейки - это, возможно, худший способ ссылаться на диапазон в Excel. Его производительность просто ужасна. Offset или просто Worksheet.Range намного лучше.
Если кому-то нужно сделать это в 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
Пример: = ПОДСТАВИТЬ (ТЕКСТ (АДРЕС (1,1000,4), ""), "1", "")
Да, я использую Excel в стране, где; используется вместо, для разделения аргументов функции в Excel. Спасибо за указание на это.
Я сделал это без функции ТЕКСТ. Какова цель функции ТЕКСТ?
@dayuloli Давненько с тех пор, как был написан этот ответ, вы правы, функция ТЕКСТ здесь не служит цели. Обновлю ответ.
Используя это в VB.Net 2005:
Private Function ColumnName(ByVal ColumnIndex As Integer) As String
Dim Name As String = ""
Name = (New Microsoft.Office.Interop.Owc11.Spreadsheet).Columns.Item(ColumnIndex).Address(False, False, Microsoft.Office.Interop.Owc11.XlReferenceStyle.xlA1)
Name = Split(Name, ":")(0)
Return Name
End Function
хорошая идея. но ужасная производительность. неявное создание объекта с последующим использованием еще четырех точек для вызова метода final Address приведет к ужасному маршаллингу взаимодействия. однозначно нужно переписать.
Уточнение исходного решения (на 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 void Foo()
{
l_ExcelApp = new Excel.ApplicationClass();
l_ExcelApp.ReferenceStyle = Excel.XlReferenceStyle.xlR1C1;
// ... now reference by R[row]C[column], Ex. A1 <==> R1C1, C6 <==> R3C6, ...
}
подробнее здесь - Ссылка на ячейки в Excel для всех! Доктор Нитин Паранджапе
Тип ApplicationClass не использовался со времен Excel 2003. Прекратите использовать плохое кодирование.
..И преобразовал в 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.
public static string ConvertToAlphaColumnReferenceFromInteger(int columnReference)
{
int baseValue = ((int)('A')) - 1 ;
string lsReturn = String.Empty;
if (columnReference > 26)
{
lsReturn = ConvertToAlphaColumnReferenceFromInteger(Convert.ToInt32(Convert.ToDouble(columnReference / 26).ToString().Split('.')[0]));
}
return lsReturn + Convert.ToChar(baseValue + (columnReference % 26));
}
Не могли бы вы добавить какие-нибудь пояснения?
Вот версия 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;
}
Вам может потребоваться преобразование в обоих направлениях, например, из адреса столбца 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 + а ^ 3 +….
Предположим, вы хотите найти общее количество состояний A в первых N пробелах. Формула для этого: A = (a) (a ^ N - 1) / (a-1). Это важно, потому что нам нужно найти пространство N, которое соответствует нашему индексу K. Если я хочу выяснить, где находится K в системе счисления, мне нужно заменить A на K и решить для N. Решение: N = log { основание a} (A (a-1) / a +1). Если я использую пример a = 10 и K = 192, я знаю, что N = 2,23804…. Это говорит мне, что K находится в начале третьего промежутка, так как он немного больше двух.
Следующим шагом будет определение того, как далеко мы находимся в текущем пространстве. Чтобы найти это, вычтите из K значение A, полученное с использованием минимального значения N. В этом примере минимальное значение N равно двум. Итак, A = (10) (10 ^ 2-1) / (10-1) = 110, как и ожидалось, когда вы объединяете состояния первых двух пробелов. Это необходимо вычесть из K, потому что эти первые 110 состояний уже были учтены в первых двух пространствах. Остается 82 государства. Итак, в этой системе счисления 192 в базе 10 представлены как 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;
}
ох, я не знаю почему, но мне нравится это решение. Ничего особенного, просто хорошее использование логики ... легко читаемый код для уровней программиста. Однако я считаю, что лучше всего указывать пустую строку в C# как string range = string.Empty;
Да, очень красивое объяснение. Но не могли бы вы также просто заявить, что это тоже не база 27? Ваше объяснение показывает это при изучении, но быстрое упоминание вверху может сэкономить время некоторым другим людям.
если вам просто нужна формула ячейки без кода, вот формула для нее:
IF(COLUMN()>=26,CHAR(ROUND(COLUMN()/26,1)+64)&CHAR(MOD(COLUMN(),26)+64),CHAR(COLUMN()+64))
public string ToBase26(int number)
{
if (number < 0) return String.Empty;
int remainder = number % 26;
int value = number / 26;
return value == 0 ?
String.Format("{0}", Convert.ToChar(65 + remainder)) :
String.Format("{0}{1}", ToBase26(value - 1), Convert.ToChar(65 + remainder));
}
Это не просто преобразование по основанию 26. Прочтите другие ответы.
Предполагая, что первый столбец равен 0. 0 вернет A, 26 вернет AA, а 702 вернет AAA. Разница между моим и первым правильным ответом заключалась в том, что он начинался с 1, а мой - с 0. Это мое понимание. Поправьте меня если я ошибаюсь.
@dirn ты ошибаешься. База с 10 до 26 преобразует 11 в A, а не 1 в A.
Такая же реализация на 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;
}
В Delphi (Паскаль):
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;
Вот как я бы сделал это на Python. Алгоритм объясняется ниже:
alph = ('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')
def labelrec(n, res):
if n<26:
return alph[n]+res
else:
rem = n%26
res = alph[rem]+res
n = n/26-1
return labelrec(n, res)
Функцию labelrec можно вызвать с номером и пустой строкой, например:
print labelrec(16383, '')
Вот почему это работает: Если бы десятичные числа были записаны так же, как столбцы таблицы Excel, цифры 0-9 были бы записаны нормально, но 10 превратилось бы в «00», а затем 20 стало бы на «10» и так далее. Отображение нескольких чисел:
0 - 0
9–9
10 - 00
20 - 10
100–90
110– 000
1110 - 0000
Итак, закономерность ясна. Начиная с места единицы, если число меньше 10, его представление такое же, как и само число, иначе вам нужно отрегулировать оставшееся число, вычитая его на 1 и рекурсивно. Вы можете остановиться, когда число меньше 10.
Та же логика применяется к числам по основанию 26 в приведенном выше решении.
P.S. Если вы хотите, чтобы числа начинались с 1, вызовите ту же функцию для номера ввода, уменьшив его на 1.
Посмотрев на все представленные здесь версии, я решил сделать одну самостоятельно, используя рекурсию.
Вот моя версия 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));
}
Вы сделали обратное тому, о чем просили, но +1 для вашего лямбда-фу.
IndexOf довольно медленный, лучше просчитать обратное отображение.
Я пытаюсь сделать то же самое на 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 .... Но, глядя на ответ Грэма, я немного запутался ...
Грэм говорит: columnName = Convert.ToChar(65 + modulo).ToString() + columnName (т.е. значение + ColName). Хасан говорит: columnName += val; (т.е. ColName + значение)
Вы добавляете новый символ вместо его добавления. Должен быть columnName = columnName + val.
В perl для входа 1 (A), 27 (AA) и т. д.
sub excel_colname {
my ($idx) = @_; # one-based column number
--$idx; # zero-based column index
my $name = "";
while ($idx >= 0) {
$name .= chr(ord("A") + ($idx % 26));
$idx = int($idx / 26) - 1;
}
return scalar reverse $name;
}
Я удивлен, что все решения пока содержат либо итерацию, либо рекурсию.
Вот мое решение, которое работает в постоянное время (без циклов). Это решение работает для всех возможных столбцов 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);
}
}
К вашему сведению: ответ @ Graham (и, вероятно, другие) более общий, чем ваш: они поддерживают 4+ символа в именах столбцов. И именно поэтому они итеративны.
Фактически, если бы они использовали неограниченное количество целых чисел, а не int, результирующее имя столбца могло бы быть произвольно длинным (например, в случае ответа python)
Если мои данные окажутся слишком большими для таблицы с 16 384 столбцами, я выстрелю себе в голову. В любом случае, Excel даже не поддерживает все возможные трехбуквенные столбцы (он обрезается в XFD, оставляя 1894 столбца). Во всяком случае, прямо сейчас. Я буду обновлять свой ответ в будущем по мере необходимости.
:) не знал этого! Мой комментарий касался теоретических свойств различных алгоритмов.
Это, наверное, самое простое и понятное решение.
Слишком сложно. Циклы - это законный способ сделать код коротким и читаемым.
+1 для отображения максимальной ссылки на ячейку - вы не поверите, как сложно найти эту информацию в Интернете.
@controlbox вот совет. Откройте новую таблицу Excel и щелкните любую ячейку. Затем, удерживая нажатой клавишу Control, щелкните стрелку вправо. Он перейдет в столбец XFD. Если вместо этого щелкнуть стрелку вниз (удерживая нажатой клавишу), вы перейдете к строке 1048576. Это быстрый способ найти максимальную ссылку на ячейку.
Этот ответ находится в 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;
}
Вот моя супер поздняя реализация на 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;
}
Решение 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;
};
Попробуйте индекс 26 и 27. Он очень близок, но отстает на единицу.
значение = Math.floor (значение / 26); должно быть значение = Math.ceil (value / 26) - 1;
(Я понимаю, что вопрос относится к C#, однако, если кому-то нужно сделать то же самое с Ява, тогда может быть полезно следующее)
Оказывается, это легко сделать с помощью класса CellReference в Джакарта POI. Кроме того, преобразование может быть выполнено в обоих направлениях.
// Convert row and column numbers (0-based) to an Excel cell reference
CellReference numbers = new CellReference(3, 28);
System.out.println(numbers.formatAsString());
// Convert an Excel cell reference back into digits
CellReference reference = new CellReference("AC4");
System.out.println(reference.getRow() + ", " + reference.getCol());
Другой способ VBA
Public Function GetColumnName(TargetCell As Range) As String
GetColumnName = Split(CStr(TargetCell.Cells(1, 1).Address), "$")(1)
End Function
Я хотел добавить свой статический класс, который я использую, для взаимодействия между индексом столбца и меткой столбца. Я использую измененный принятый ответ для своего метода 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
Эти мои коды для преобразования определенного числа (начало индекса с 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));
}
+1 для отображения максимальной ссылки на ячейку в ваших тестах (XFD) - вы не поверите, насколько сложно найти эту информацию в Интернете.
Совпадающая и элегантная версия Рубин:
def col_name(col_idx)
name = ""
while col_idx>0
mod = (col_idx-1)%26
name = (65+mod).chr + name
col_idx = ((col_idx-mod)/26).to_i
end
name
end
Мне просто нужно было проделать эту работу сегодня, моя реализация использует рекурсию:
private static string GetColumnLetter(string colNumber)
{
if (string.IsNullOrEmpty(colNumber))
{
throw new ArgumentNullException(colNumber);
}
string colName = String.Empty;
try
{
var colNum = Convert.ToInt32(colNumber);
var mod = colNum % 26;
var div = Math.Floor((double)(colNum)/26);
colName = ((div > 0) ? GetColumnLetter((div - 1).ToString()) : String.Empty) + Convert.ToChar(mod + 65);
}
finally
{
colName = colName == String.Empty ? "A" : colName;
}
return colName;
}
При этом учитывается число, поступающее в виде строки, метод и числа, начинающиеся с "0" (A = 0)
Реализация Objective-C:
-(NSString*)getColumnName:(int)n {
NSString *name = @"";
while (n>0) {
n--;
char c = (char)('A' + n%26);
name = [NSString stringWithFormat:@"%c%@",c,name];
n = n/26;
}
return name;
}
Внедрение SWIFT:
func getColumnName(n:Int)->String{
var columnName = ""
var index = n
while index>0 {
index--
let char = Character(UnicodeScalar(65 + index%26))
columnName = "\(char)\(columnName)"
index = index / 26
}
return columnName
}
Ответ основан на: https://stackoverflow.com/a/4532562/2231118
Реализация NodeJS:
/**
* getColumnFromIndex
* Helper that returns a column value (A-XFD) for an index value (integer).
* The column follows the Common Spreadsheet Format e.g., A, AA, AAA.
* See https://stackoverflow.com/questions/181596/how-to-convert-a-column-number-eg-127-into-an-excel-column-eg-aa/3444285#3444285
* @param numVal: Integer
* @return String
*/
getColumnFromIndex: function(numVal){
var dividend = parseInt(numVal);
var columnName = '';
var modulo;
while (dividend > 0) {
modulo = (dividend - 1) % 26;
columnName = String.fromCharCode(65 + modulo) + columnName;
dividend = parseInt((dividend - modulo) / 26);
}
return columnName;
},
Спасибо Преобразование алфавита столбца Excel (например, AA) в число (например, 25). И наоборот:
/**
* getIndexFromColumn
* Helper that returns an index value (integer) for a column value (A-XFD).
* The column follows the Common Spreadsheet Format e.g., A, AA, AAA.
* See https://stackoverflow.com/questions/9905533/convert-excel-column-alphabet-e-g-aa-to-number-e-g-25
* @param strVal: String
* @return Integer
*/
getIndexFromColumn: function(val){
var base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
for (i = 0, j = val.length - 1; i < val.length; i += 1, j -= 1) {
result += Math.pow(base.length, j) * (base.indexOf(val[i]) + 1);
}
return result;
}
Видел еще один ответ VBA - это можно сделать в excel-vba с однострочным UDF:
Function GetColLetter(ByVal colID As Integer) As String
GetColLetter = Split(Cells(1, colID).Address, "$")(1)
End Function
Хотя я опаздываю в игру, Ответ Грэма далеко не оптимален. В частности, вам не нужно использовать 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;
}
Миниатюра Microsoft Excel, формула быстрого и грязного.
Привет,
Вот один из способов получить заголовок столбца символов Excel из числа ....
Я создал формулу для ячейки Excel.
(т.е. я отказался от программирования на VBA.)
Формула смотрит на ячейку, в которой есть число, и сообщает вам, что это за столбец - буквами.
На прикрепленном изображении:
Я сделал это таким образом, чтобы доказать, что формула работает, чтобы вы могли посмотреть на результат формулы, посмотреть на заголовок столбца выше и легко визуально убедиться, что формула работает. :-)
= СЦЕПИТЬ (MID ("_ abcdefghijklmnopqrstuvwxyz", (IF (MOD (K1,26)> 0, INT (K1 / 26) +1, (INT (K1 / 26))))), 1), MID ("abcdefghijklmnopqrstuvwxyz", ЕСЛИ (MOD (K1,26) = 0,26, MOD (K1,26)), 1))
Подчеркивание использовалось для целей отладки - чтобы вы знали, что на самом деле есть пробел и что оно работает правильно.
С этой формулой выше - что бы вы ни поместили в K1 - формула сообщит вам, каким будет заголовок столбца.
Формула в ее нынешнем виде состоит только из двух цифр (ZZ), но ее можно изменить, добавив третью букву (ZZZ).
F# версия каждого способа
let rec getExcelColumnName x = if x<26 then int 'A'+x|>char|>string else (x/26-1|>c)+ c(x%26)
простите за минимизацию, работал над лучшей версией https://stackoverflow.com/a/4500043/57883
// return values start at 0
let getIndexFromExcelColumnName (x:string) =
let a = int 'A'
let fPow len i =
Math.Pow(26., len - 1 - i |> float)
|> int
let getValue len i c =
int c - a + 1 * fPow len i
let f i = getValue x.Length i x.[i]
[0 .. x.Length - 1]
|> Seq.map f
|> Seq.sum
|> fun x -> x - 1
Просто добавьте простую двухстрочную реализацию 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);
}
Это вопрос для всех остальных, а также для перенаправления 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)
Это будет очень похоже на многих языках, поэтому вы можете адаптировать его по мере необходимости.
Опять же, используйте это только в том случае, если вы на 100% уверены, что у вас не будет больше 26 столбцов.
Спасибо за ответы здесь !! помог мне придумать эти вспомогательные функции для некоторого взаимодействия с API Google Sheets, над которым я работаю в Elixir / Phoenix
вот что я придумал (возможно, можно было бы использовать дополнительную проверку и обработку ошибок)
В Эликсире:
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
Извините, это 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))
Пройдены эти тестовые случаи:
Это версия javascript в соответствии с кодом Грэма.
function (columnNumber) {
var dividend = columnNumber;
var columnName = "";
var modulo;
while (dividend > 0) {
modulo = (dividend - 1) % 26;
columnName = String.fromCharCode(65 + modulo) + columnName;
dividend = parseInt((dividend - modulo) / 26);
}
return columnName;
};
Вот код Грэма на Powershell:
function ConvertTo-ExcelColumnID {
param (
[parameter(Position = 0,
HelpMessage = "A 1-based index to convert to an excel column ID. e.g. 2 => 'B', 29 => 'AC'",
Mandatory = $true)]
[int]$index
);
[string]$result = '';
if ($index -le 0 ) {
return $result;
}
while ($index -gt 0) {
[int]$modulo = ($index - 1) % 26;
$character = [char]($modulo + [int][char]'A');
$result = $character + $result;
[int]$index = ($index - $modulo) / 26;
}
return $result;
}
Большинство предыдущих ответов верны. Вот еще один способ преобразования номера столбца в столбцы Excel. решение довольно простое, если рассматривать это как базовое преобразование. Просто преобразуйте номер столбца в основание 26, так как всего 26 букв. Вот как это можно сделать:
шаги:
установить столбец как частное
вычтите единицу из переменной частного (из предыдущего шага), потому что нам нужно получить таблица ascii, где 97 будет.
разделите на 26 и получите остаток.
вот код, который это делает :)
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())
Уже более 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)));
}
Вот мое решение на питоне
import math
num = 3500
row_number = str(math.ceil(num / 702))
letters = ''
num = num - 702 * math.floor(num / 702)
while num:
mod = (num - 1) % 26
letters += chr(mod + 65)
num = (num - 1) // 26
result = row_number + ("".join(reversed(letters)))
print(result)
Машинопись
function lengthToExcelColumn(len: number): string {
let dividend: number = len;
let columnName: string = '';
let modulo: number = 0;
while (dividend > 0) {
modulo = (dividend - 1) % 26;
columnName = String.fromCharCode(65 + modulo).toString() + columnName;
dividend = Math.floor((dividend - modulo) / 26);
}
return columnName;
}
Кажется, что многие ответы намного сложнее, чем необходимо. Вот общий ответ Ruby, основанный на описанной выше рекурсии:
В этом ответе хорошо то, что он не ограничен 26 символами английского алфавита. Вы можете определить любой диапазон в константе COLUMNS, и он будет работать правильно.
# vim: ft=ruby
class Numeric
COLUMNS = ('A'..'Z').to_a
def to_excel_column(n = self)
n < 1 ? '' : begin
base = COLUMNS.size
to_excel_column((n - 1) / base) + COLUMNS[(n - 1) % base]
end
end
end
# verify:
(1..52).each { |i| printf "%4d => %4s\n", i, i.to_excel_column }
Это напечатает следующее, например:
1 => A
2 => B
3 => C
....
33 => AG
34 => AH
35 => AI
36 => AJ
37 => AK
38 => AL
39 => AM
40 => AN
41 => AO
42 => AP
43 => AQ
44 => AR
45 => AS
46 => AT
47 => AU
48 => AV
49 => AW
50 => AX
51 => AY
52 => AZ
Это частый вопрос, который задают при тестировании кодирования. у него есть некоторые ограничения: максимальное количество столбцов в строке = 702 вывод должен иметь номер строки + имя столбца, например. для 703 ответ 2А. (примечание: я только что изменил существующий код из другого ответа) вот код того же:
static string GetExcelColumnName(long columnNumber)
{
//max number of column per row
const long maxColPerRow = 702;
//find row number
long rowNum = (columnNumber / maxColPerRow);
//find tierable columns in the row.
long dividend = columnNumber - (maxColPerRow * rowNum);
string columnName = String.Empty;
long modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (int)((dividend - modulo) / 26);
}
return rowNum+1+ columnName;
}
}
T-SQL (SQL СЕРВЕР 18)
Копия решения на первой странице
CREATE FUNCTION dbo.getExcelColumnNameByOrdinal(@RowNum int)
RETURNS varchar(5)
AS
BEGIN
DECLARE @dividend int = @RowNum;
DECLARE @columnName varchar(max) = '';
DECLARE @modulo int;
WHILE (@dividend > 0)
BEGIN
SELECT @modulo = ((@dividend - 1) % 26);
SELECT @columnName = CHAR((65 + @modulo)) + @columnName;
SELECT @dividend = CAST(((@dividend - @modulo) / 26) as int);
END
RETURN
@columnName;
END;
Вот более простое решение для индекса столбца с нулевым индексом
public static string GetColumnIndexNumberToExcelColumn(int columnIndex)
{
int offset = columnIndex % 26;
int multiple = columnIndex / 26;
int initialSeed = 65;//Represents column "A"
if (multiple == 0)
{
return Convert.ToChar(initialSeed + offset).ToString();
}
return $"{Convert.ToChar(initialSeed + multiple - 1)}{Convert.ToChar(initialSeed + offset)}";
}
Не забывая, что есть ограничения на количество доступных столбцов. Например. * Excel 2003 (v11) доходит до IV, 2 ^ 8 или 256 столбцов). * Excel 2007 (v12) может содержать до XFD, 2 ^ 14 или 16384 столбца.