Число по цифрам в 1С: деление с остатком и разбор строки

· 9 мин

Содержание

Разложить число на отдельные цифры во встроенном языке 1С можно двумя путями: арифметикой — через остаток от деления % 10 и целую часть Цел(), или строкой — перевести число в текст и обойти его посимвольно функцией Сред(). Оба дают на выходе набор цифр; разница в скорости и в том, что удобнее дописывать под задачу.

Арифметику берут, когда нужны именно числа для расчёта (сумма разрядов, контрольная цифра). Строку — когда важен порядок символов и не жаль пары лишних миллисекунд. Ниже — рабочий код для обоих, плюс то, обо что чаще всего спотыкаются: отрицательные значения, ноль, дробная часть и коварный разделитель групп.

Деление с остатком: достаём цифры арифметикой

Идея простая. Остаток от деления на 10 — это последняя цифра числа. Отбросили её целочисленным делением на 10 — и повторяем, пока число не обнулится.

Во встроенном языке за это отвечают два инструмента:

  • 🔢 Число % 10 — остаток от деления, то есть младший разряд (12345 % 10 = 5);
  • ✂️ Цел(Число / 10) — целая часть от деления на 10, то есть «число без последней цифры» (Цел(12345 / 10) = 1234).

Цикл разбора выглядит так:

Число = 12345;
Пока Число > 0 Цикл
    Цифра = Число % 10;        // 5, затем 4, 3, 2, 1
    Сообщить(Цифра);
    Число = Цел(Число / 10);   // 1234, 123, 12, 1, 0
КонецЦикла;

Цифры выходят с конца: сначала 5, потом 4 и так до 1. Если нужен естественный порядок слева направо, не разворачивайте результат отдельным циклом — вставляйте каждую цифру в начало массива:

Массив = Новый Массив;
Пока Число > 0 Цикл
    Массив.Вставить(0, Число % 10);  // всегда в начало
    Число = Цел(Число / 10);
КонецЦикла;

Вставить(0, Значение) добавляет элемент по индексу 0, сдвигая остальные. Для 12345 получаем массив [1, 2, 3, 4, 5] в правильном порядке.

Способ работает только с целой частью. Дробь он молча игнорирует: Цел(12.7 / 10) отбросит всё после запятой на первом же шаге. Как разобрать дробную часть — ниже, в разделе про особые случаи.

Через строку: преобразование и посимвольный обход

Второй путь — превратить число в строку и пройти по символам. Читается нагляднее, и порядок цифр сразу правильный, слева направо.

Но здесь прячется главная ловушка. Прямое Строка(12345) вернёт не "12345", а "12 345" — с неразрывным пробелом как разделителем групп разрядов. Посимвольный обход тогда наткнётся на пробел, а Число(" ") даст ноль или ошибку.

Для перевода числа в строку используйте Формат с параметром ЧГ=0 — он отключает разделитель групп.

Число = 12345;
Стр = Формат(Число, "ЧГ=0");          // "12345", без пробелов

Массив = Новый Массив;
Для Поз = 1 По СтрДлина(Стр) Цикл
    Символ = Сред(Стр, Поз, 1);        // по одному символу
    Массив.Добавить(Число(Символ));    // "1" -> 1
КонецЦикла;

Что здесь что:

  • Формат(Число, "ЧГ=0") — число в строку без форматирования разрядов;
  • СтрДлина(Стр) — количество символов;
  • Сред(Стр, Поз, 1) — один символ начиная с позиции Поз (нумерация с 1);
  • Число(Символ) — обратно в числовой тип, если нужна именно цифра, а не знак.

Если цифры нужны как текст (например, для вывода в макет), Число(Символ) можно опустить и складывать в массив сами символы.

Формат(Число, "ЧГ=0") для дробного числа сохранит разделитель целой и дробной части (запятую или точку). Для работы только с целой частью сначала примените Цел(), иначе запятая попадёт в массив как отдельный символ.

Отрицательные числа, ноль и дробная часть

Три случая, на которых падает «наивный» код, если про них не подумать заранее.

Знак «минус». У отрицательного числа % 10 и деление ведут себя иначе, а в строке впереди появится символ -. Проще всего убрать знак в самом начале. Отдельной функции модуля в языке нет — используйте условие:

Число = ?(Число < 0, -Число, Число);   // модуль числа

?(Условие, ТогдаЗначение, ИначеЗначение) — тернарный оператор: вернёт -Число, если число меньше нуля, и само число в остальных случаях.

Ноль. Цикл Пока Число > 0 для нуля не выполнится ни разу — массив останется пустым. Если 0 должен давать одну цифру 0, обработайте его явно до цикла:

Если Число = 0 Тогда
    Массив.Добавить(0);
    Возврат Массив;
КонецЕсли;

Дробная часть. Решите заранее, что с ней делать:

  • 🧹 цифры нужны только из целой части — отбросьте дробь через Цел(Число);
  • 🔍 нужны и цифры после запятой — удобнее строковый способ: переведите число в строку с Формат(Число, "ЧРД=.") (точка как разделитель), разбейте по этому разделителю через СтрРазделить, и разбирайте целую и дробную части по отдельности.

Жёстко прибивать точность (например, два знака после запятой) лучше тоже форматом: Формат(Число, "ЧДЦ=2; ЧРД=.; ЧГ=0"). Так вы не зависите от того, сколько знаков реально хранится в переменной.

Ведущие нули. Числовой тип их не хранит: 007 и 7 для 1С — одно и то же число, разбор даст [7]. Если ведущие нули значимы (табельный номер, артикул, код из файла обмена), не превращайте значение в число вообще — храните и разбирайте его как строку. Для фиксированной длины с нулями слева используйте формат Формат(Число, "ЧЦ=5; ЧВН=; ЧГ=0") — получите строку "00007".

Универсальная функция: цифры числа в массив

Собираем всё вместе — модуль, ноль, порядок слева направо. Функция возвращает массив цифр целой части любого числа.

Функция ЦифрыЧисла(Знач Число) Экспорт
    Массив = Новый Массив;

    // модуль и только целая часть
    Число = Цел(?(Число < 0, -Число, Число));

    Если Число = 0 Тогда
        Массив.Добавить(0);
        Возврат Массив;
    КонецЕсли;

    Пока Число > 0 Цикл
        Массив.Вставить(0, Число % 10);
        Число = Цел(Число / 10);
    КонецЦикла;

    Возврат Массив;
КонецФункции

Проверка на примерах:

ВходРезультат
12345[1, 2, 3, 4, 5]
-408[4, 0, 8]
0[0]
70.9[7, 0]

Параметр объявлен как Знач Число — значит, внутри функции мы меняем локальную копию, а переменная вызывающего кода остаётся нетронутой. Без Знач вы бы обнулили исходное число у вызывающей стороны.

Если на выходе нужны не числа, а строка из цифр подряд ("12345") — замените тело на строковый способ из второго раздела и возвращайте Формат(Цел(Число), "ЧГ=0").

Когда нужна не вся разбивка, а одна цифра на известной позиции, полный цикл не нужен. Комбинация деления и остатка достаёт конкретный разряд:

Единицы = Число % 10;                 // младший разряд
Десятки = Цел(Число / 10) % 10;       // второй справа
Сотни   = Цел(Число / 100) % 10;      // третий справа

Для 4728: единицы — 8, десятки — 2, сотни — 7. Общая формула разряда: делим на 10 в нужной степени и берём остаток от деления на 10. Это дешевле, чем собирать весь массив ради одной цифры.

Какой из двух способов брать под конкретную задачу:

Что выбрать — арифметику или строку?

Что быстрее: сравнение двух способов

На единичных вызовах разницы вы не заметите — оба способа отрабатывают за доли миллисекунды. Она проявляется в циклах на десятки и сотни тысяч чисел: массовая обработка, формирование больших печатных форм, проверка номеров при загрузке.

КритерийДеление с остаткомЧерез строку
Скорость в массовых циклахВыше: только арифметикаНиже: перевод в строку + Сред()
ЧитаемостьЧуть сложнееНагляднее
Дробная частьТребует отдельной логикиДостаётся из строки проще
Исходник — строкаНужен лишний перевод в числоРаботает напрямую

Практический вывод: в горячем цикле на большом объёме используйте арифметику, в разовых операциях и отчётах — тот способ, который понятнее поддерживать.

Отдельно про то, что реально тормозит, а не кажется: не вызывайте Формат() и не создавайте новый массив там, где можно обойтись. Если нужна только сумма цифр — не собирайте массив вовсе, складывайте остатки прямо в цикле. Лишняя коллекция на каждой итерации дороже самой арифметики.

Где это нужно на практике

Разбор числа на цифры — не самоцель, а шаг в типовых задачах. Четыре частых сценария.

Сумма цифр и контрольные разряды. Классика — проверка контрольного числа в кодах (номера документов, идентификаторы). Складываем цифры прямо в цикле, без массива:

Функция СуммаЦифр(Знач Число)
    Число = Цел(?(Число < 0, -Число, Число));
    Сумма = 0;
    Пока Число > 0 Цикл
        Сумма = Сумма + Число % 10;
        Число = Цел(Число / 10);
    КонецЦикла;
    Возврат Сумма;
КонецФункции

Для 1234 функция вернёт 10 (1 + 2 + 3 + 4). На этой же механике строят алгоритмы проверки идентификаторов, где каждую цифру умножают на свой весовой коэффициент.

Сумма прописью. Чтобы перевести число в текст, его режут на триады (группы по три разряда справа) и обрабатывают каждую. Поразрядный разбор — основа таких алгоритмов, хотя для готовой суммы прописью в типовых конфигурациях есть штатная функция.

Маскирование номеров. Показать только последние цифры, остальные скрыть — например, в логах или на форме. Удобнее строкой: берём хвост через Прав(), начало заменяем на звёздочки:

Стр = Формат(Номер, "ЧГ=0");
Хвост = Прав(Стр, 4);                              // последние 4 цифры
Маска = СтрПовторить("*", СтрДлина(Стр) - 4);      // звёзды на остальное
Результат = Маска + Хвост;                         // "************1234"

СтрПовторить("*", N) повторяет символ N раз, Прав(Стр, 4) берёт последние четыре символа. Для короткого номера (4 символа и меньше) добавьте проверку, иначе длина маски уйдёт в отрицательное число.

Поразрядный вывод в макет. Когда каждую цифру суммы нужно вывести в отдельную ячейку (бланки, квитанции), массив цифр из универсальной функции ложится прямо в область макета по одному элементу на колонку.

Для готовых задач не изобретайте велосипед: сумма прописью, форматирование разрядов и округление уже решены функциями Формат, Окр и библиотечными методами. Ручной разбор на цифры нужен там, где логика нестандартная.

Частые вопросы

Почему `Строка(Число)` даёт пробел между цифрами?
Это разделитель групп разрядов (неразрывный пробел). Чтобы его убрать, переводите число в строку через Формат(Число, "ЧГ=0") — параметр ЧГ=0 отключает группировку.
Как получить цифры в обычном порядке, а не с конца?
При делении с остатком цифры выходят справа налево. Вставляйте каждую в начало массива методом Вставить(0, Цифра) — тогда порядок будет естественным, слева направо, без разворота.
Есть ли в 1С функция модуля числа?
Отдельной функции Abs во встроенном языке нет. Модуль получают условием: ?(Число < 0, -Число, Число). Для разбора на цифры знак обычно убирают в самом начале.
Что делать с дробной частью?
Если нужны цифры только целой части — отбросьте дробь через Цел(Число). Если важны и знаки после запятой — переведите число в строку с фиксированной точностью (Формат(Число, "ЧДЦ=2; ЧРД=.")) и разберите целую и дробную части отдельно.
Какой способ быстрее на больших объёмах?
Деление с остатком: оно обходится чистой арифметикой без перевода в строку и вызова Сред(). На единичных числах разница незаметна, в циклах на сотни тысяч элементов — ощутима.
Как узнать количество цифр в числе?
Самое простое — длина строки без разделителей: СтрДлина(Формат(Цел(Число), "ЧГ=0")). Для 12345 вернёт 5. Знак и дробную часть предварительно уберите через Цел() и модуль, иначе они попадут в подсчёт.