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