Строка или число: проверка типа и безопасное преобразование во встроенном языке 1С
Содержание
Чтобы определить, строка или число лежит в переменной 1С, сравните её тип с эталоном: ТипЗнч(Значение) = Тип("Число") или ТипЗнч(Значение) = Тип("Строка"). Если же тип заведомо строковый и нужно понять, записано ли в строке число, — попробуйте преобразовать её через Число() внутри Попытка…Исключение или проверьте символы заранее.
Обычно вопрос всплывает при загрузке из Excel, CSV или внешнего API. Вместо суммы приходит текст "1 234,50", код падает с Преобразование значения к типу Число не может быть выполнено, а сравнение "5" = 5 молча возвращает Ложь. Решается это тремя приёмами: проверкой типа, нормализацией текста и преобразованием с контролем ошибки.
Чем строка отличается от числа в языке 1С
Во встроенном языке 1С «Строка» и «Число» — разные примитивные типы. Переменная не типизирована: сегодня в ней 5, завтра "5". Платформа сама не угадывает, что вы имели в виду.
Что из этого следует на практике:
- 🔢 Сравнение не приводит типы.
"5" = 5даётЛожь, хотя на экране обе величины выглядят одинаково. - ➕ Сложение приводит к типу левого операнда.
5 + "5"вернёт число10, а"5" + 5— строку"55". - ⚠️ Неудачное приведение — исключение.
5 + "пять"остановит выполнение с ошибкой преобразования. - 🧾 Число в строке теряет ведущие нули.
Число("0012")вернёт12. Для ИНН, КПП, артикулов и кодов это недопустимо — их храните строкой.
Отдельный случай — неявное преобразование числа в строку при склейке. "Сумма: " + 1234.5 даст текст с разделителем групп разрядов (неразрывный пробел), то есть "Сумма: 1 234,5". Если результат потом снова разбирается в число, он уже не преобразуется. Склеивайте явно: Формат(Сумма, "ЧГ=0") или СтрШаблон().
Как определить тип переменной функцией ТипЗнч
ТипЗнч() возвращает объект «Тип» для любого значения. Его сравнивают с типом, полученным через Тип("ИмяТипа"). Это точная и быстрая проверка: она отвечает на вопрос «что лежит в переменной», а не «как это выглядит».
Если ТипЗнч(Значение) = Тип("Число") Тогда
// арифметика без опасений
ИначеЕсли ТипЗнч(Значение) = Тип("Строка") Тогда
// текст: возможно, число нужно извлечь
ИначеЕсли Значение = Неопределено Тогда
// значение не передано
Иначе
ВызватьИсключение "Неожиданный тип: " + Строка(ТипЗнч(Значение));
КонецЕсли;
Когда ТипЗнч действительно нужен:
- 📥 Параметры общих функций. В процедуру из общего модуля могут прийти и число, и строка, и
Неопределено. - 🧩 Реквизиты составного типа. Например, значение дополнительного реквизита может быть строкой, числом, датой или ссылкой.
- 📊 Ячейки, прочитанные через COM. Excel через
COMОбъектотдаёт числа числами, а текстовые ячейки — строками, в одном столбце бывает и то, и другое. - 🌐 Результат разбора JSON. После
ПрочитатьJSON()числа приходят как «Число», а числа в кавычках — как «Строка».
Не сравнивайте типы через строковое представление: Строка(ТипЗнч(Х)) = "Число". Представление зависит от языка интерфейса, а сравнение с Тип("Число") — нет.
Пустая строка — тоже строка: ТипЗнч("") = Тип("Строка") вернёт Истина. А ЗначениеЗаполнено(0) и ЗначениеЗаполнено("") оба вернут Ложь. Поэтому «заполненность» и «тип» проверяйте раздельно.
Проверка строки на число через Попытка-Исключение
ТипЗнч не скажет, можно ли превратить "125.40" в число: для него это просто строка. Самый короткий способ — попробовать преобразовать и перехватить ошибку.
Функция СтрокаВЧисло(Знач Текст) Экспорт
Текст = СокрЛП(Текст);
Текст = СтрЗаменить(Текст, Символы.НПП, "");
Текст = СтрЗаменить(Текст, " ", "");
Текст = СтрЗаменить(Текст, ",", ".");
Если Текст = "" Тогда
Возврат Неопределено;
КонецЕсли;
Попытка
Возврат Число(Текст);
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
Функция возвращает число или Неопределено. Вызывающий код проверяет результат одной строкой:
Сумма = СтрокаВЧисло(ЯчейкаСумма);
Если Сумма = Неопределено Тогда
СообщениеОбОшибке = СтрШаблон("Строка %1: в поле «Сумма» не число", НомерСтроки);
КонецЕсли;
Плюсы подхода — краткость и то, что правила разбора задаёт сама платформа. Минусы тоже есть:
- 🐢 Исключение — дорогая операция. На десятках тысяч строк, где половина значений текстовые, загрузка заметно замедлится.
- 🐞 Отладчик с включённой остановкой по ошибке будет останавливаться на каждой нечисловой строке.
- 🔒 Внутри транзакции ловить исключения как штатную логику — плохая практика: ошибки при работе с базой помечают транзакцию как сломанную, и дальше запись не пройдёт. Проверку данных выносите до начала транзакции.
Не оставляйте блок Исключение пустым без возврата признака ошибки. Иначе в учёт молча попадёт ноль, и найти источник будет тяжело.
Проверка без исключений: ОписаниеТипов и разбор по символам
ОписаниеТипов.ПривестиЗначение
Объект «ОписаниеТипов» умеет приводить значение к своему типу без исключений. Если строку нельзя преобразовать, вернётся значение по умолчанию — для числа это 0.
ОписаниеЧисла = Новый ОписаниеТипов("Число");
Сумма = ОписаниеЧисла.ПривестиЗначение("125.40"); // 125,4
Мусор = ОписаниеЧисла.ПривестиЗначение("abc"); // 0
Ловушка очевидна: строка "0" и строка "abc" дают одинаковый результат. Метод подходит, когда ноль для пустых и битых значений — приемлемое поведение, например при загрузке необязательного количества. Если ноль нужно отличать от ошибки, используйте другой способ.
Можно задать разрядность и точность через квалификаторы — тогда значение заодно округлится и уложится в формат реквизита:
КЧ = Новый КвалификаторыЧисла(15, 2);
ОписаниеСуммы = Новый ОписаниеТипов("Число", КЧ);
Посимвольная проверка
Когда нужен честный ответ «да/нет» без исключений, проверьте символы. Функция ниже допускает знак минус в начале, один десятичный разделитель (точку или запятую) и пробелы между разрядами.
Функция ЭтоЧисловаяСтрока(Знач Текст) Экспорт
Текст = СокрЛП(Текст);
Текст = СтрЗаменить(Текст, Символы.НПП, "");
Текст = СтрЗаменить(Текст, " ", "");
Текст = СтрЗаменить(Текст, ",", ".");
Если Лев(Текст, 1) = "-" Тогда
Текст = Сред(Текст, 2);
КонецЕсли;
Если Текст = "" Или Текст = "." Тогда
Возврат Ложь;
КонецЕсли;
ЕстьРазделитель = Ложь;
Для Позиция = 1 По СтрДлина(Текст) Цикл
Символ = Сред(Текст, Позиция, 1);
Если Символ = "." Тогда
Если ЕстьРазделитель Тогда
Возврат Ложь;
КонецЕсли;
ЕстьРазделитель = Истина;
ИначеЕсли СтрНайти("0123456789", Символ) = 0 Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
Пример: "-1 234,50" → Истина, "12.3.4" → Ложь, "1e5" → Ложь, "—" (тире из пустой ячейки Excel) → Ложь.
Функция СтрНайти() доступна с платформы 8.3.6. Если в конфигурации включён режим совместимости ниже этой версии, замените её на Найти().
Сравнение способов проверки
| Способ | Что проверяет | Когда брать | Риски |
|---|---|---|---|
ТипЗнч(Х) = Тип("Число") | Тип значения в переменной | Параметры функций, составные реквизиты, JSON, COM | Не видит число внутри строки |
Число() в Попытка…Исключение | Можно ли преобразовать строку | Разовые проверки, небольшие объёмы | Медленно на больших объёмах, мешает отладке |
ОписаниеТипов.ПривестиЗначение() | Приводит без ошибок | Необязательные поля, где 0 допустим | Не отличает "0" от мусора |
| Посимвольный разбор | Формат строки | Массовая загрузка, валидация до записи | Свои правила нужно поддерживать |
ТИПЗНАЧЕНИЯ() в запросе | Тип поля составного типа | Отбор на стороне СУБД | Работает только с хранимыми типами |
Для загрузки файлов хорошо работает связка: сначала ЭтоЧисловаяСтрока() для отбора и понятного сообщения пользователю, затем Число() уже без перехвата исключения — строка гарантированно корректна.
Разделители, пробелы и знаки: почему Число() не принимает «1 234,50»
Главный источник ошибок — не тип, а формат. Одна и та же сумма приходит из разных систем по-разному:
| Источник | Как выглядит | Что мешает |
|---|---|---|
Выгрузка из 1С через Формат() | 1 234,50 | Неразрывный пробел между разрядами |
| Excel, русская локаль | 1234,50 или 1 234,50 | Запятая, иногда обычный пробел |
| CSV из банка или маркетплейса | 1234.50 | Обычно без проблем |
| API, JSON в кавычках | "1234.50" | Строка вместо числа |
| Ручной ввод | 1234,5, 1.234,50 | Пробелы по краям, точка как разделитель тысяч |
Порядок нормализации перед преобразованием:
- Обрежьте пробелы по краям:
СокрЛП(). - Удалите неразрывные пробелы
Символы.НППи обычные пробелы внутри числа. - Замените запятую на точку — с точкой как десятичным разделителем
Число()работает предсказуемо. - Только после этого вызывайте
Число()или проверку по символам.
Формат 1.234,50 (точка — тысячи, запятая — дробь) после замены запятой превратится в 1.234.50 и будет отклонён. Это правильно: угадывать формат автоматически опасно, сумма может увеличиться в тысячу раз. Если такой источник реален, договоритесь о формате выгрузки или разбирайте его отдельной веткой.
Для строгого машинного формата есть XMLЗначение(Тип("Число"), Текст). Он принимает только точку и не терпит пробелов, зато однозначен. Подходит для данных из XML и SOAP; при неверной строке вызывает исключение.
Целое или дробное — отдельный вопрос. После преобразования проверьте Цел(Сумма) = Сумма. Для количества штучного товара это частая валидация: 2.5 упаковки обычно ошибка ввода.
Проверка типа в запросе и в составных реквизитах
Если значение хранится в базе в реквизите составного типа, фильтровать его в цикле не нужно — это делает запрос. В языке запросов есть функция ТИПЗНАЧЕНИЯ() и конструкция ТИП():
ВЫБРАТЬ
Т.Ссылка,
Т.Значение
ИЗ
Справочник.Номенклатура.ДополнительныеРеквизиты КАК Т
ГДЕ
ТИПЗНАЧЕНИЯ(Т.Значение) = ТИП(ЧИСЛО)
Чтобы получить число из составного поля для арифметики в самом запросе, используйте ВЫРАЗИТЬ(Т.Значение КАК ЧИСЛО(15, 2)). Строки при этом в число не превратятся: ВЫРАЗИТЬ работает с типами, которые уже хранятся в поле, а не разбирает текст.
Проверить тип на уровне метаданных — например, может ли реквизит вообще хранить число — помогает СодержитТип():
ТипРеквизита = Метаданные.Справочники.Номенклатура.Реквизиты.Вес.Тип;
Если ТипРеквизита.СодержитТип(Тип("Число")) Тогда
// реквизит допускает число
КонецЕсли;
Это полезно в универсальных обработках загрузки, где поле файла сопоставляется с реквизитом по имени.
Как писать проверки типов быстро и без сюрпризов
Несколько приёмов, которые экономят время на больших объёмах и упрощают сопровождение:
- ⚡ Вынесите эталонные типы из цикла.
ТипЧисло = Тип("Число");до цикла, а внутри —ТипЗнч(Х) = ТипЧисло. На сотнях тысяч итераций разница заметна. - 🧰 Одна функция на проект. Положите
СтрокаВЧисло()иЭтоЧисловаяСтрока()в общий модуль с признаками «Клиент» и «Сервер», чтобы правила разбора не расходились между формами. - 🚦 Проверяйте на входе. Валидируйте файл целиком до записи объектов: пользователь получает список всех ошибок сразу, а не по одной после каждой попытки.
- 🏷 Храните коды строкой. ИНН, ОКТМО, артикулы, номера телефонов — тип «Строка» с фиксированной длиной. Числовой тип съест ведущие нули.
- 📏 Задавайте разрядность. Приводите суммы через
ОписаниеТиповс квалификатором(15, 2), чтобы1234.567не прошло в реквизит с точностью 2 без вашего ведома. - 🧪 Держите тестовый набор строк.
"","0","-0,5","1 234,50","12.3.4","abc","—"— прогоняйте через функцию после каждой правки.
Проверьте себя — какие результаты вернёт платформа:
Частые вопросы
Как проверить, что значение — целое число?
Цел(Х) = Х. Для отрицательных чисел условие работает так же.Почему число из табличного документа не распознаётся?
Символы.НПП и пробелы, замените запятую на точку и только потом преобразуйте.Можно ли проверить тип «Число или Строка» одним условием?
Новый ОписаниеТипов("Число, Строка").СодержитТип(ТипЗнч(Х)). Описание типов удобно создать один раз и переиспользовать.Чем ТипЗнч отличается от ЗначениеЗаполнено?
Почему после Строка(Число) в тексте появляются пробелы?
Формат(Х, "ЧГ=0") или XMLСтрока(Х) — последняя всегда ставит точку.