Проверка типа значения в коде 1С: число перед вами или нет

· 9 мин

Содержание

Тип значения в 1С проверяют функцией ТипЗнч() и сравнением с Тип("Число"):

Если ТипЗнч(Значение) = Тип("Число") Тогда
    // перед нами число
КонецЕсли;

Это работает для любой переменной во встроенном языке 1С:Предприятие 8.3 — неважно, пришло значение из реквизита документа, из параметра процедуры или из коллекции. А вот проверка «число или нет» для строки, введённой пользователем, — отдельная задача: строка "125" остаётся строкой, пока вы её не разберёте. Ниже — оба случая, плюс Неопределено, NULL, параметры функций и проверка типа в запросах и СКД.

Тема относится к платформе в целом, поэтому пути и код одинаковы в Бухгалтерии, ЗУП, УТ и любой другой конфигурации на 8.3.

Функция ТипЗнч и сравнение с Тип()

ТипЗнч(Значение) возвращает объект типа Тип. Его сравнивают с эталонным типом, который даёт функция Тип("ИмяТипа"):

Значение = 125;

Если ТипЗнч(Значение) = Тип("Число") Тогда
    Сообщить("Число");
ИначеЕсли ТипЗнч(Значение) = Тип("Строка") Тогда
    Сообщить("Строка");
КонецЕсли;

Имена примитивных типов пишутся строкой на русском языке :

Что проверяемВыражение
ЧислоТип("Число")
СтрокаТип("Строка")
Булево (истина/ложь)Тип("Булево")
ДатаТип("Дата")
НеопределеноТип("Неопределено")
NULLТип("NULL")
Ссылка на справочникТип("СправочникСсылка.Контрагенты")

Для ссылочных типов к имени добавляется конкретный объект метаданных — СправочникСсылка.Номенклатура, ДокументСсылка.РеализацияТоваровУслуг и так далее.

Чаще всего проверку оборачивают в короткую функцию и держат в общем модуле:

Функция ЭтоЧисло(Значение) Экспорт
    Возврат ТипЗнч(Значение) = Тип("Число");
КонецФункции

ТипЗнч смотрит именно на тип, а не на содержимое. ТипЗнч(0) вернёт Тип("Число"), ТипЗнч("") — Тип("Строка"). Пустое число остаётся числом, пустая строка — строкой.

Когда допустимых типов несколько, перечислять их через длинную цепочку ИЛИ неудобно. Заранее сложите нужные типы в Соответствие и проверяйте одно вхождение:

ЧисловыеТипы = Новый Соответствие;
ЧисловыеТипы.Вставить(Тип("Число"), Истина);
ЧисловыеТипы.Вставить(Тип("Булево"), Истина);

Если ЧисловыеТипы.Получить(ТипЗнч(Значение)) = Истина Тогда
    // значение одного из перечисленных типов
КонецЕсли;

Такой справочник типов удобно собрать один раз и переиспользовать — например, при разборе реквизитов составного типа, где вариантов много.

Когда строка должна оказаться числом

Самая частая ситуация — пользователь ввёл значение в поле типа «Строка», и нужно понять, можно ли считать его числом. Встроенной функции ЭтоЧисло(Строка) в платформе нет, поэтому проверку пишут вручную. Два рабочих способа.

Способ 1. Разобрать по символам. Подходит для целых чисел и работает быстро — без исключений:

Функция СтрокаЭтоЦелоеЧисло(Знач Строка) Экспорт
    Строка = СокрЛП(Строка);
    Если ПустаяСтрока(Строка) Тогда
        Возврат Ложь;
    КонецЕсли;
    Для Позиция = 1 По СтрДлина(Строка) Цикл
        Символ = Сред(Строка, Позиция, 1);
        Если Найти("0123456789", Символ) = 0 Тогда
            Возврат Ложь;
        КонецЕсли;
    КонецЦикла;
    Возврат Истина;
КонецФункции

Здесь Найти("0123456789", Символ) возвращает позицию символа в наборе цифр или 0, если символа там нет. Встретили не-цифру — сразу выходим с Ложь. Если нужно разрешить минус и дробную часть, набор допустимых символов расширяют и отдельно проверяют, что разделитель и знак стоят на своих местах.

Способ 2. Попробовать преобразовать. Функция XMLЗначение(Тип("Число"), Строка) разбирает строку как число и выбрасывает исключение, если это не число. Ловим исключение через Попытка:

Функция СтрокаПреобразуетсяВЧисло(Знач Строка, Результат) Экспорт
    Попытка
        Результат = XMLЗначение(Тип("Число"), СокрЛП(Строка));
        Возврат Истина;
    Исключение
        Возврат Ложь;
    КонецПопытки;
КонецФункции

Удобно тем, что заодно отдаёт само число в параметр Результат — не нужно второй раз преобразовывать строку после успешной проверки.

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

XMLЗначение ждёт число в XML-формате: разделитель дробной части — точка, никаких пробелов и разделителей групп. Строку "1 250,50" (пробел и запятая) он не примет. Перед проверкой уберите пробелы и замените запятую на точку: СтрЗаменить(Строка, ",", ".").

Попытка с перехватом исключения заметно медленнее обычного кода. В цикле на тысячи итераций разбор по символам (способ 1) предпочтительнее, чем ловля исключений.

Неопределено, NULL и пустые значения — в чём разница

Три «пустых» значения в 1С путают чаще всего, а ведут себя они по-разному.

  • 🔹 Неопределено — значение не задано. Так инициализируется переменная, если метод ничего не вернул, или необязательный параметр, который не передали.
  • 🔹 NULL — приходит только из запросов: например, левое соединение не нашло пару, и поле второй таблицы оказалось пустым. В обычном коде NULL просто так не появляется.
  • 🔹 0 и "" — это уже заполненные значения типов «Число» и «Строка», а не отсутствие значения.

Проверяют каждое из них своим сравнением:

Если Значение = Неопределено Тогда
    // переменная не инициализирована
КонецЕсли;

Если Значение = NULL Тогда
    // пришло из запроса, пары не нашлось
КонецЕсли;

Когда нужно одной проверкой отсеять «любую пустоту», берут ЗначениеЗаполнено(). Она возвращает Ложь для Неопределено, NULL, нуля, пустой строки, пустой даты и пустой ссылки:

Если НЕ ЗначениеЗаполнено(Значение) Тогда
    // пусто: 0, "", Неопределено, NULL, пустая ссылка...
КонецЕсли;

Не путайте задачи: ТипЗнч отвечает «какого это типа», ЗначениеЗаполнено — «есть ли тут содержательное значение». Для числа ТипЗнч(0) = Тип("Число") истинно, а ЗначениеЗаполнено(0) — ложно.

Отдельная ловушка — результат метода Найти у коллекций и Получить у соответствия: если элемент не найден, возвращается Неопределено, а не ошибка. Поэтому после поиска значение проверяют на тип или на Неопределено, прежде чем с ним работать:

Найденное = Соответствие.Получить(Ключ);
Если Найденное = Неопределено Тогда
    // ключа в соответствии нет
ИначеЕсли ТипЗнч(Найденное) = Тип("Число") Тогда
    // нашли и это число
КонецЕсли;

Если сразу вызвать арифметику на возможном Неопределено, получите ошибку преобразования типа во время выполнения — её как раз и предотвращает проверка.

Число есть — а дальше: целое, дробное, со знаком

Часто после проверки «это число» нужно ещё одно условие: целое перед нами или с дробной частью, положительное или отрицательное. Готовых функций ЭтоЦелое или ЭтоОтрицательное в платформе нет, но всё решается арифметикой.

Целое или дробное. Функция Цел(Число) отбрасывает дробную часть. Если результат равен исходному значению — число целое:

Если Число = Цел(Число) Тогда
    Сообщить("Целое");
Иначе
    Сообщить("Есть дробная часть");
КонецЕсли;

Знак числа. Отрицательное, ноль или положительное определяют обычным сравнением:

Если Число > 0 Тогда
    Знак = "положительное";
ИначеЕсли Число < 0 Тогда
    Знак = "отрицательное";
Иначе
    Знак = "ноль";
КонецЕсли;

Дробная часть отдельно. Её получают вычитанием целой части: ДробнаяЧасть = Число - Цел(Число). Это пригодится, когда надо проверить, что сумма задана с точностью до копейки, а не с лишними знаками.

Для денежных сумм помните про разрядность реквизита: число 10.005 в поле с двумя знаками после запятой платформа округлит при записи. Проверять «ровность» суммы лучше до записи в реквизит.

Проверка входных параметров процедур и функций

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

Процедура РассчитатьСумму(Количество, Цена) Экспорт
    Если ТипЗнч(Количество) <> Тип("Число")
        Или ТипЗнч(Цена) <> Тип("Число") Тогда
        ВызватьИсключение "Количество и цена должны быть числом";
    КонецЕсли;
    // основной расчёт
КонецПроцедуры

Для необязательного параметра удобен шаблон с Неопределено: если значение не передали, подставляем значение по умолчанию.

Функция ОкруглитьСумму(Сумма, Точность = Неопределено) Экспорт
    Если Точность = Неопределено Тогда
        Точность = 2;
    КонецЕсли;
    Возврат Окр(Сумма, Точность);
КонецФункции

Такой контроль особенно важен в экспортных методах общих модулей: их вызывает чужой код, и гарантий по типам у вас нет. Одна проверка ТипЗнч на входе экономит часы на поиск причины, когда в расчёт прилетела строка или Неопределено.

Проверка типа в запросах и системе компоновки данных

В языке запросов свои функции. Тип выражения проверяет ТИПЗНАЧЕНИЯ(), а эталонный тип задаёт ТИП():

ВЫБРАТЬ
    Таблица.Поле КАК Значение
ИЗ
    РегистрСведений.ДополнительныеРеквизиты КАК Таблица
ГДЕ
    ТИПЗНАЧЕНИЯ(Таблица.Поле) = ТИП(Число)

Это нужно для полей составного типа — когда в одном реквизите может лежать и число, и строка, и ссылка. Условие в ГДЕ оставит только те строки, где значение — число.

Чтобы не просто отфильтровать, а привести значение к числу, применяют ВЫРАЗИТЬ. Если фактический тип не совпал, выражение вернёт NULL:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Таблица.Поле КАК Число(15, 2)) КАК Сумма
ИЗ
    РегистрСведений.ДополнительныеРеквизиты КАК Таблица

Отдельно обрабатывают NULL от соединений — функцией ЕСТЬNULL, которая подставляет значение по умолчанию:

ЕСТЬNULL(Продажи.Сумма, 0) КАК Сумма

А для ссылочных полей тип проверяют оператором ССЫЛКА:

ГДЕ Таблица.Поле ССЫЛКА Справочник.Контрагенты

Разделяйте два разных NULL-инструмента. Условие ПОЛЕ ЕСТЬ NULL (с пробелом, как оператор) проверяет, что значение пустое после соединения. Функция ЕСТЬNULL(Поле, 0) (слитно) подставляет замену. Первое — для фильтра в ГДЕ, второе — чтобы в ВЫБРАТЬ вместо пустоты оказался ноль и дальнейшая арифметика не сломалась.

В системе компоновки данных отдельной «магии» нет: проще всего решить вопрос типов прямо в запросе набора данных — теми же ВЫРАЗИТЬ, ТИПЗНАЧЕНИЯ и ЕСТЬNULL. Тогда в поля компоновки придут уже очищенные значения нужного типа, и вычисляемым полям СКД не придётся гадать, что им досталось.

Производительность и выбор метода

Для одиночной проверки разница в скорости незаметна. Она появляется в циклах на десятки тысяч итераций — тогда способ выбирают осознанно.

  • ⚡ ТипЗнч(X) = Тип("Число") — самый быстрый вариант для проверки типа.
  • 🐢 Попытка + XMLЗначение — удобно, но медленно: исключения дороги. Не ставьте такой разбор в горячий цикл.
  • 📏 Разбор строки по символам — быстрее исключений, когда проверяете массу строк.

Ещё один приём для циклов: Тип("Число") при каждом вызове разбирает строковый литерал заново. Если проверяете тип внутри цикла, вычислите эталонный тип один раз до цикла и сравнивайте с переменной:

ТипЧисло = Тип("Число");
Для Каждого Элемент Из Коллекция Цикл
    Если ТипЗнч(Элемент) = ТипЧисло Тогда
        // обрабатываем число
    КонецЕсли;
КонецЦикла;

Так вы убираете из тела цикла лишний разбор строки "Число" на каждой итерации.

Проверьте себя
Что вернёт `ТипЗнч(Неопределено)`?
Откуда в переменной может появиться `NULL`?
Какой проверкой отсеять сразу 0, пустую строку, `Неопределено` и `NULL`?

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

Есть ли в 1С готовая функция `ЭтоЧисло`?
Встроенной нет. Для уже типизированной переменной используют ТипЗнч(Значение) = Тип("Число"), а для разбора строки пишут свою функцию — через перебор символов или через XMLЗначение в блоке Попытка.
Как проверить, что в строке дробное число с запятой?
XMLЗначение ждёт точку как разделитель, поэтому сначала приведите строку: уберите пробелы и выполните СтрЗаменить(Строка, ",", "."), и только потом проверяйте.
Что вернёт `ТипЗнч(0)` и `ТипЗнч("")`?
Тип("Число") и Тип("Строка") соответственно. Тип значения не зависит от того, заполнено оно или пусто, — ноль и пустая строка остаются числом и строкой.
Почему сравнение `Значение = NULL` не всегда ловит пустоту?
NULL приходит только из запросов. В обычном коде незаданная переменная — это Неопределено, а не NULL. Если нужно отсечь любую пустоту разом, берите ЗначениеЗаполнено(), а в запросе — ЕСТЬNULL.
Можно ли одним `ТипЗнч` проверить сразу несколько типов?
Нет, ТипЗнч сравнивают с одним типом. Для нескольких вариантов перечисляют условия через ИЛИ либо заранее складывают допустимые типы в соответствие и проверяют вхождение.