При разработке механизмов обмена данными, формировании печатных форм или создании уникальных идентификаторов программисты часто сталкиваются с задачей генерации последовательностей, использующих буквы латинского алфавита. Самый известный пример такой системы — наименование колонок в таблицах Excel: сначала идут одиночные буквы от A до Z, затем двухбуквенные комбинации AA, AB... AZ, затем BA и так далее. Рассмотрим, как реализовать этот механизм в 1С и в чем заключаются математические особенности такой нумерации.
На первый взгляд кажется, что перед нами обычная 26-ричная система счисления, где буквам A-Z соответствуют цифры 0-25. Однако при попытке стандартного перевода мы столкнемся с проблемой "ведущего нуля". Проанализируем ситуацию: в позиционных системах счисления (например, десятичной) числа 0, 00 и 000 математически равны. Если мы назначим букву A нулем, то последовательности A, AA и AAA будут означать одно и то же число, что недопустимо для уникальной маркировки.
Эта специфическая последовательность называется биективной системой счисления с основанием 26. В ней отсутствует символ «ноль», а каждое натуральное число (1, 2, 3...) соответствует строго одной уникальной строке символов. Рассмотрим подробнее, как это реализовать программно.
Разберем по шагам логику, которая позволяет правильно переходить от числа к буквенному представлению. Ключевым моментом здесь является "сдвиг" на единицу при расчете остатка. Проанализируем код функции, предложенный в обсуждении:
Функция НомерДляМаркировки(Знач Значение = 0, Нотация = 26, СтрокаСистемы = "ABCDEFGHIJKLMNOPQRSTUVWXYZ") Экспорт
// 1 -> A
// 26 -> Z
// 27 -> AA
Результат = "";
Пока Значение > 0 Цикл
// Вычисляем символ текущего разряда со сдвигом
Результат = Сред(СтрокаСистемы, (Значение - 1) % Нотация + 1, 1) + Результат;
Если Значение <= Нотация Тогда
Прервать;
КонецЕсли;
// Уменьшаем значение для перехода к следующему разряду
Значение = Цел((Значение - 1) / Нотация);
Если Значение = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Выясним причину использования конструкции (Значение - 1). Именно это вычитание позволяет нам корректно обрабатывать границы разрядов. Например, когда Значение равно 26, операция (26-1) % 26 + 1 даст нам 26-й символ алфавита (Z). Без этого вычитания остаток от деления 26 на 26 был бы равен 0, что характерно для классических систем счисления, но ломает логику Excel-подобных рядов.
Посмотрим на пример, как ведет себя обычная функция перевода (где A=0, B=1... BA=26):
BZBAКак мы видим, в классическом варианте число 26 превращается в BA (аналог 10 в десятичной системе), пропуская AA. В нашей же задаче 26 должно быть Z, а 27 — AA. Рассмотрим результаты работы правильного алгоритма:
НомерДляМаркировки(1) -> "A"НомерДляМаркировки(26) -> "Z"НомерДляМаркировки(27) -> "AA"НомерДляМаркировки(52) -> "AZ"НомерДляМаркировки(53) -> "BA"Проанализируем еще один способ реализации, который опирается на предварительное вычисление количества знаков в итоговой строке. Этот метод удобен, если нужно четко понимать, сколько "ячеек" займет номер.
Функция ПолучитьБуквенныйКод(Знач ЧислоДляПеревода) Экспорт
Модуль = 26;
НачКод = КодСимвола("A");
// Определяем количество цифр (разрядов)
КоличествоРазрядов = 1;
Пока ЧислоДляПеревода >= Pow(Модуль, КоличествоРазрядов) Цикл
// В биективной системе каждый новый разряд "съедает" объем комбинаций предыдущих
ЧислоДляПеревода = ЧислоДляПеревода - Pow(Модуль, КоличествоРазрядов);
КоличествоРазрядов = КоличествоРазрядов + 1;
КонецЦикла;
Результат = "";
Для Инд = 1 По КоличествоРазрядов Цикл
Результат = Символ(НачКод + ЧислоДляПеревода % Модуль) + Результат;
ЧислоДляПеревода = Цел(ЧислоДляПеревода / Модуль);
КонецЦикла;
Возврат Результат;
КонецФункции
В данном примере мы используем встроенные функции 1С КодСимвола и Символ, что избавляет от необходимости хранить строку-алфавит. Это решение более изящно с точки зрения использования ресурсов платформы.
Рассмотрим ситуацию, когда такая нумерация становится необходимой. Автор темы столкнулся с проблемой при переносе данных: инвойсы (счета) имели одинаковые номера, но относились к разным периодам. Чтобы сделать их уникальными в новой системе, потребовалось добавлять суффиксы. Обычных 26 букв не хватило, так как за несколько лет накопилось более сотни повторов одного и того же номера.
Основные области применения:
COM-объект или при записи в ТабличныйДокумент для обращения к области.Важно помнить, что если вы используете эту систему для формирования имен файлов или элементов справочников, стоит учитывать регистр символов и возможность расширения алфавита (например, использование цифр и букв одновременно), что превратит систему в биективную 36-ричную систему. Принцип реализации при этом останется неизменным — главное соблюдать правило сдвига на единицу при расчете каждого разряда.