Как исправить ошибку «Преобразование к типу число не может быть выполнено» в 1С при работе со скрытыми символами

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Каждый разработчик на платформе 1С:Предприятие рано или поздно сталкивается с загадочным поведением системы, когда визуально абсолютно корректная строка отказывается преобразовываться в числовой тип. Мы пишем стандартный код, проверяем значение переменной в отладчике — там находится обычная единица или другое число, окруженное пробелами. Мы пробуем выполнить преобразование этого значения «в лоб» в окне вычисления выражений, и всё работает. Однако в самом коде программа упорно падает с ошибкой «Преобразование к типу число не может быть выполнено».

В этой статье мы подробно проанализируем причины этой проблемы, разберем физическую суть происходящего в памяти компьютера и на платформе 1С, а также выработаем несколько надежных и эффективных способов решения — от быстрого исправления «на лету» до системного устранения первопричины.

В чем суть проблемы: как отладчик нас обманывает

Давайте детально разберем ситуацию, которая часто описывается на технических форумах. Разработчик считывает данные из внешнего источника (например, текстового файла, CSV, XML или сторонней базы данных) в список значений или таблицу значений. В процессе обработки данных выполняется следующий код:


// Попытка преобразования элемента списка значений в число
ИскомоеЧисло = Число(Рез[0].Значение);

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

  1. В окне «Табло» выражение Рез[0].Значение возвращает строку " 1 ". Визуально это обычное строковое представление единицы с пробелами по краям.
  2. Выражение Число(" 1 "), написанное вручную в окне вычисления выражений, отрабатывает абсолютно корректно и возвращает число 1. Платформа 1С умеет автоматически игнорировать ведущие и замыкающие пробелы при преобразовании типов.
  3. Но как только мы пишем Число(Рез[0].Значение), платформа снова выдает ошибку.

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

Главный виновник: Символ(0) и путаница с кодировками (Unicode vs ANSI)

Основной причиной появления таких «невидимых» препятствий является некорректное чтение файлов, сохраненных в двухбайтовой кодировке (например, UTF-16 или Unicode), в режиме однобайтового чтения (например, ANSI / Windows-1251).

В кодировке UTF-16 для представления каждого символа используется как минимум 2 байта (16 бит). Для стандартных символов латиницы, цифр и знаков препинания первый байт содержит код символа, а второй байт заполняется нулями (или наоборот, в зависимости от порядка байтов — Little Endian или Big Endian). Если мы пытаемся прочитать такой файл как однобайтовый текстовый файл в кодировке Windows-1251, платформа 1С воспринимает каждый байт как отдельный символ.

В результате этого процесса каждый второй символ в считанной строке превращается в так называемый NULL-символ — символ с кодом 0 (в терминологии 1С это Символ(0)). Визуально в отладчике строка выглядит как " 1 ", но фактически ее структура в памяти выглядит следующим образом:

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

Как провести диагностику строки в отладчике

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


ПроверяемаяСтрока = Рез[0].Значение;
РезультатАнализа = "";

Для Индекс = 1 По СтрДлина(ПроверяемаяСтрока) Цикл
    ТекущийСимвол = Сред(ПроверяемаяСтрока, Индекс, 1);
    Код = КодСимвола(ТекущийСимвол);
    РезультатАнализа = РезультатАнализа + "Символ №" + Индекс + ": '" + ТекущийСимвол + "' (Код: " + Код + ")" + Символы.ПС;
КонецЦикла;

Сообщить(РезультатАнализа);

Если в окне сообщений мы увидим, что среди символов присутствуют элементы с кодом 0, наша гипотеза подтвердилась. Также нередко там оказываются символы с кодом 160 (неразрывный пробел) или управляющие символы перевода строки и табуляции.

Способ 1. Устранение первопричины — правильное чтение файла

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

Рассмотрим пример правильной настройки объекта ЧтениеТекста. Если мы знаем, что исходный файл записан в кодировке Unicode (UTF-16), мы должны инициализировать объект чтения следующим образом:


// Явное указание кодировки UTF-16 при чтении файла
ПутьКФайлу = "C:\Data\import.txt";
ФайлДанных = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.UTF16);

// Читаем строки файла в правильной кодировке
ТекущаяСтрока = ФайлДанных.ПрочитатьСтроку();
Пока ТекущаяСтрока <> Неопределено Цикл
    // В данном случае строка сразу прочитается корректно, без лишних нулевых байтов
    ОбрататьСтроку(ТекущаяСтрока);
    ТекущаяСтрока = ФайлДанных.ПрочитатьСтроку();
КонецЦикла;

Если кодировка файла заранее неизвестна, можно использовать автоматическое определение кодировки платформой 1С. Для этого при создании объекта ЧтениеТекста не указывайте кодировку вообще или передайте пустую строку — в большинстве случаев 1С успешно распознает маркер последовательности байтов (BOM) в начале файла и применит нужную кодировку автоматически.

Способ 2. Быстрое исправление — удаление Символ(0) через СтрЗаменить

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

Для удаления нулевых символов мы воспользуемся встроенной функцией СтрЗаменить(). Рассмотрим пример кода:


// Удаляем все нулевые символы из строки значения
ЧистаяСтрока = СтрЗаменить(Рез[0].Значение, Символ(0), "");

// Теперь преобразование к числу пройдет успешно
Попытка
    ИскомоеЧисло = Число(ЧистаяСтрока);
Исключение
    // На случай, если в строке содержатся другие некорректные символы
    ИскомоеЧисло = 0;
    ЗаписьЖурналаРегистрации("ПреобразованиеТипов", УровеньЖурналаРегистрации.Предупреждение, , , "Не удалось преобразовать строку: " + ЧистаяСтрока);
КонецПопытки;

Замена Символ(0) на пустую строку полностью удаляет вредоносные нулевые байты, не изменяя при этом значащие цифры и стандартные пробелы.

Способ 3. Универсальная функция глубокой очистки строки

На практике, помимо символов с кодом 0, в строках могут содержаться и другие нежелательные элементы. К ним относятся:

Для гарантированного приведения любой «грязной» строки к числу мы рекомендуем использовать универсальную функцию глубокой очистки. Разберем её реализацию по шагам:


// Функция производит безопасное приведение строки к числовому типу,
// удаляя скрытые символы, пробелы всех видов и корректируя разделители.
Функция ПолучитьЧислоИзСтрокиБезопасно(Знач ВходнаяСтрока) Экспорт
    
    Если Не ЗначениеЗаполнено(ВходнаяСтрока) Тогда
        Возврат 0;
    КонецЕсли;
    
    // Приводим к строковому типу на случай, если на вход пришел другой тип
    РабочаяСтрока = Строка(ВходнаяСтрока);
    
    // Шаг 1. Избавляемся от NULL-символов (результат ошибок кодировок UTF)
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, Символ(0), "");
    
    // Шаг 2. Удаляем неразрывные пробелы (код 160)
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, Символы.НПП, "");
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, Символ(160), "");
    
    // Шаг 3. Удаляем стандартные управляющие символы
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, Символы.ВК, "");
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, Символы.ПС, "");
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, Символы.Таб, "");
    
    // Шаг 4. Удаляем обычные пробелы (код 32)
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, " ", "");
    
    // Шаг 5. Приводим разделитель дробной части к единому стандарту.
    // Если в системе разделителем установлена запятая, а в строке точка (или наоборот),
    // заменим её на правильный системный символ.
    РабочаяСтрока = СтрЗаменить(РабочаяСтрока, ".", ","); 
    
    // Шаг 6. Финальное преобразование с обработкой исключений
    Попытка
        Возврат Число(РабочаяСтрока);
    Исключение
        // Если преобразовать все же не удалось (например, в строке буквы),
        // попробуем сделать еще одну замену разделителя в обратную сторону
        Попытка
            РабочаяСтрока = СтрЗаменить(РабочаяСтрока, ",", ".");
            Возврат Число(РабочаяСтрока);
        Исключение
            Возврат 0; // Возвращаем дефолтное значение при полной неудаче
        КонецПопытки;
    КонецПопытки;
    
КонецФункции

Данная функция полностью закрывает проблему "грязных" данных из внешних источников и исключает аварийные остановки конфигурации из-за ошибок преобразования типов.

Способ 4. Использование регулярных выражений (RegExp)

Если загружаемые строки содержат не только скрытые символы, но и сопутствующий текст (например, обозначение валюты " руб.", " USD", или другие литеры), то самым надежным способом извлечения числа будет использование механизма регулярных выражений через COM-объект. Мы можем составить паттерн, который оставит в строке только цифры, знак минуса и разделительную точку или запятую.

Проанализируем пример реализации такого подхода:


// Извлечение числового значения из строки произвольного формата
Функция ИзвлечьЧислоРегулярнымВыражением(Знач ВходнаяСтрока) Экспорт
    
    // Сначала очищаем от NULL-символов, так как COM-объект RegExp может некорректно с ними работать
    ЧистаяСтрока = СтрЗаменить(Строка(ВходнаяСтрока), Символ(0), "");
    
    Попытка
        РегВыражение = Новый COMОбъект("VBScript.RegExp");
    Исключение
        // На случай работы на сервере Linux, где COM-объекты недоступны
        Возврат ПолучитьЧислоИзСтрокиБезопасно(ЧистаяСтрока);
    КонецПопытки;
    
    РегВыражение.MultiLine = Истина;
    РегВыражение.Global    = Истина;
    РегВыражение.IgnoreCase = Истина;
    
    // Шаблон: оставляем только цифры, дефис (минус) и точку/запятую
    РегВыражение.Pattern = "[^0-9.,-]";
    
    // Заменяем все неподходящие символы на пустые строки
    РезультатСтрокой = РегВыражение.Replace(ЧистаяСтрока, "");
    
    // Дополнительно заменяем точку на запятую для соответствия стандарту 1С
    РезультатСтрокой = СтрЗаменить(РезультатСтрокой, ".", ",");
    
    Попытка
        Возврат Число(РезультатСтрокой);
    Исключение
        Возврат 0;
    КонецПопытки;
    
КонецФункции

Использование регулярных выражений гарантирует высочайшую степень отказоустойчивости алгоритма импорта данных, отсекая любые артефакты форматирования, скрытые символы Юникода и случайные опечатки операторов ввода.

Мы надеемся, что рассмотренные методы помогут вам раз и навсегда решить проблему с ошибками приведения типов в ваших прикладных решениях 1С:Предприятие!

← На главную