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

· 9 мин

Содержание

Чтобы определить, строка или число лежит в переменной 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") или СтрШаблон().

Как определить тип переменной функцией ТипЗнч

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

Если ТипЗнч(Значение) = Тип("Число") Тогда
    // арифметика без опасений
ИначеЕсли ТипЗнч(Значение) = Тип("Строка") Тогда
    // текст: возможно, число нужно извлечь
ИначеЕсли Значение = Неопределено Тогда
    // значение не передано
Иначе
    ВызватьИсключение "Неожиданный тип: " + Строка(ТипЗнч(Значение));
КонецЕсли;

Когда ТипЗнч действительно нужен:

  1. 📥 Параметры общих функций. В процедуру из общего модуля могут прийти и число, и строка, и Неопределено.
  2. 🧩 Реквизиты составного типа. Например, значение дополнительного реквизита может быть строкой, числом, датой или ссылкой.
  3. 📊 Ячейки, прочитанные через COM. Excel через COMОбъект отдаёт числа числами, а текстовые ячейки — строками, в одном столбце бывает и то, и другое.
  4. 🌐 Результат разбора 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. Обрежьте пробелы по краям: СокрЛП().
  2. Удалите неразрывные пробелы Символы.НПП и обычные пробелы внутри числа.
  3. Замените запятую на точку — с точкой как десятичным разделителем Число() работает предсказуемо.
  4. Только после этого вызывайте Число() или проверку по символам.

Формат 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", "—" — прогоняйте через функцию после каждой правки.

Проверьте себя — какие результаты вернёт платформа:

Проверьте себя
Что вернёт выражение "5" = 5?
Что вернёт выражение 5 + "5"?
Что вернёт Новый ОписаниеТипов("Число").ПривестиЗначение("abc")?
Какой тип вернёт ТипЗнч("")?

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

Как проверить, что значение — целое число?
Сначала убедитесь, что тип «Число», затем сравните Цел(Х) = Х. Для отрицательных чисел условие работает так же.
Почему число из табличного документа не распознаётся?
Текст ячейки табличного документа — всегда строка, часто с неразрывными пробелами. Удалите Символы.НПП и пробелы, замените запятую на точку и только потом преобразуйте.
Можно ли проверить тип «Число или Строка» одним условием?
Да: Новый ОписаниеТипов("Число, Строка").СодержитТип(ТипЗнч(Х)). Описание типов удобно создать один раз и переиспользовать.
Чем ТипЗнч отличается от ЗначениеЗаполнено?
ТипЗнч отвечает, какого типа значение. ЗначениеЗаполнено — отличается ли оно от пустого значения своего типа: для числа это 0, для строки — пустая строка.
Почему после Строка(Число) в тексте появляются пробелы?
Платформа форматирует число с разделителем групп разрядов. Для машинного текста используйте Формат(Х, "ЧГ=0") или XMLСтрока(Х) — последняя всегда ставит точку.