Как исправить ошибку «Процедура или функция с указанным именем не определена (Массив)» и построить надежный импорт данных в 1С?

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

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

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

Причины возникновения ошибки синтаксического анализатора

В процессе компиляции обработчика загрузки система выводит диагностическое сообщение вида: {Обработка.ИмпортИзФайлаТовары.Форма.Форма.Форма(27,28)}: Процедура или функция с указанным именем не определена (Массив) при выполнении строки присвоения НоваяСтрокаТЗ.Описание = Массив (2);.

Корень проблемы заключается в правилах синтаксиса встроенного языка платформы 1С:Предприятие:

Способы корректного обращения к элементам коллекции Массив

Коллекция Массив в платформе 1С:Предприятие представляет собой индексированный список значений, где нумерация элементов всегда начинается с нуля: от 0 до Количество() - 1. Для извлечения и изменения значений элементов используются два штатных способа:

  1. Оператор индексного доступа через квадратные скобки: Основной, наиболее компактный и общепринятый способ. Значение индекса передается внутри квадратных скобок:
    
    НоваяСтрокаТЗ.Код = Массив[0];
    НоваяСтрокаТЗ.Товар = Массив[1];
    НоваяСтрокаТЗ.Описание = Массив[2];
    
    Индекс должен быть неотрицательным целым числом. Указание дробного числа вызовет ошибку при выполнении.
  2. Использование метода коллекции «Получить()»: Функциональный синтаксис, при котором индекс передается в метод Получить() в круглых скобках:
    
    НоваяСтрокаТЗ.Код = Массив.Получить(0);
    НоваяСтрокаТЗ.Товар = Массив.Получить(1);
    НоваяСтрокаТЗ.Описание = Массив.Получить(2);
    
    Оба способа абсолютно эквивалентны по скорости выполнения и поведению при возникновении нештатных ситуаций.

Критические уязвимости и скрытые дефекты типовых алгоритмов импорта

Устранение круглых скобок решает проблему компиляции, однако в исходной реализации загрузки сохраняется целый ряд архитектурных и алгоритмических дефектов, приводящих к сбоям в рабочей среде:

Пошаговый план модернизации механизма загрузки

Для создания отказоустойчивой обработки импорта рекомендуется придерживаться следующей последовательности действий:

  1. Организация передачи файла с клиента на сервер: На клиенте вызывается диалог выбора файла, после чего файл загружается во временное хранилище с помощью метода ПоместитьФайлНаСервер().
  2. Чтение данных через временный файл: На сервере двоичные данные извлекаются из хранилища и сохраняются во временный файл через ПолучитьИмяВременногоФайла(), после чего безопасно открываются объектом ТекстовыйДокумент с явным указанием кодировки (ANSI или UTF-8).
  3. Парсинг с валидацией структуры: Каждая строка разбивается вызовом СтрРазделить с флагом сохранения пустых значений. Проверяется соответствие количества элементов ожидаемому числу колонок. Некорректные строки фиксируются в журнале или отчете об ошибках без остановки всего процесса.
  4. Кэширование нормативно-справочной информации: Перед записью новых объектов создается кэш существующих элементов справочников (городов, поставщиков) с помощью коллекций Соответствие, что исключает повторные запросы к базе по одинаковым строкам.
  5. Выполнение операций в транзакции: Массовое создание и запись элементов оборачиваются в единую транзакцию с помощью методов НачатьТранзакцию() и ЗафиксироватьТранзакцию(), что обеспечивает целостность базы данных и ускоряет запись в десятки раз.

Эталонный пример реализации модуля импорта

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


&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
    ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
    ПараметрыДиалога.Заголовок = "Выберите файл для импорта данных";
    ПараметрыДиалога.Фильтр = "Текстовые файлы (*.csv;*.txt)|*.csv;*.txt";
    
    ОповещениеОЗавершении = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
    НачатьПомещениеФайлаНаСервер(ОповещениеОЗавершении, , , ПараметрыДиалога, УникальныйИдентификатор);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораФайла(ОписаниеПомещенногоФайла, ДополнительныеПараметры) Экспорт
    Если ОписаниеПомещенногоФайла = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    РезультатИмпорта = ПрочитатьИЗагрузитьНаСервере(ОписаниеПомещенногоФайла.Адрес);
    ПоказатьОповещениеПользователя("Импорт завершен", , РезультатИмпорта);
КонецПроцедуры

&НаСервере
Функция ПрочитатьИЗагрузитьНаСервере(АдресХранилища)
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресХранилища);
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("txt");
    ДвоичныеДанные.Записать(ИмяВременногоФайла);
    
    ТекстовыйДок = Новый ТекстовыйДокумент;
    ТекстовыйДок.Прочитать(ИмяВременногоФайла, КодировкаТекста.UTF8);
    УдалитьФайлы(ИмяВременногоФайла);
    
    ВсегоСтрок = ТекстовыйДок.КоличествоСтрок();
    Если ВсегоСтрок < 2 Тогда
        Возврат "Файл не содержит строк данных для загрузки.";
    КонецЕсли;
    
    // Кэш ссылок для оптимизации поиска в цикле
    КэшГородов = Новый Соответствие;
    КэшФирм = Новый Соответствие;
    ЗагруженоТоваров = 0;
    Ошибок = 0;
    
    НачатьТранзакцию();
    Попытка
        Для НомерСтроки = 2 По ВсегоСтрок Цикл
            ТекущаяСтрока = ТекстовыйДок.ПолучитьСтроку(НомерСтроки);
            Если ПустаяСтрока(СокрЛП(ТекущаяСтрока)) Тогда
                Продолжить;
            КонецЕсли;
            
            // Включаем пустые колонки для соблюдения структуры
            Колонки = СтрРазделить(ТекущаяСтрока, ";", Истина);
            Если Колонки.Количество() < 6 Тогда
                Ошибок = Ошибок + 1;
                Продолжить;
            КонецЕсли;
            
            // Безопасное обращение по индексу через квадратные скобки
            СтрКод = СокрЛП(Колонки[0]);
            СтрТовар = СокрЛП(Колонки[1]);
            СтрОписание = СокрЛП(Колонки[2]);
            СтрКатегория = СокрЛП(Колонки[3]);
            СтрПоставщик = СокрЛП(Колонки[4]);
            СтрГород = СокрЛП(Колонки[5]);
            
            // Получение или создание города с кэшированием
            СсылкаГород = КэшГородов[СтрГород];
            Если СсылкаГород = Неопределено И Не ПустаяСтрока(СтрГород) Тогда
                СсылкаГород = Справочники.Города.НайтиПоНаименованию(СтрГород, Истина);
                Если Не ЗначениеЗаполнено(СсылкаГород) Тогда
                    ОбъектГород = Справочники.Города.СоздатьЭлемент();
                    ОбъектГород.Наименование = СтрГород;
                    ОбъектГород.Записать();
                    СсылкаГород = ОбъектГород.Ссылка;
                КонецЕсли;
                КэшГородов.Вставить(СтрГород, СсылкаГород);
            КонецЕсли;
            
            // Получение или создание поставщика с кэшированием
            СсылкаПоставщик = КэшФирм[СтрПоставщик];
            Если СсылкаПоставщик = Неопределено И Не ПустаяСтрока(СтрПоставщик) Тогда
                СсылкаПоставщик = Справочники.Фирмы.НайтиПоНаименованию(СтрПоставщик, Истина);
                Если Не ЗначениеЗаполнено(СсылкаПоставщик) Тогда
                    ОбъектФирма = Справочники.Фирмы.СоздатьЭлемент();
                    ОбъектФирма.Наименование = СтрПоставщик;
                    ОбъектФирма.Записать();
                    СсылкаПоставщик = ОбъектФирма.Ссылка;
                КонецЕсли;
                КэшФирм.Вставить(СтрПоставщик, СсылкаПоставщик);
            КонецЕсли;
            
            // Поиск и заполнение товара
            ТоварСсылка = Справочники.Товары.НайтиПоНаименованию(СтрТовар, Истина);
            Если Не ЗначениеЗаполнено(ТоварСсылка) Тогда
                ОбъектТовар = Справочники.Товары.СоздатьЭлемент();
                ОбъектТовар.Наименование = СтрТовар;
                Если Не ПустаяСтрока(СтрКод) Тогда
                    ОбъектТовар.Код = СтрКод;
                КонецЕсли;
                ОбъектТовар.Описание = СтрОписание;
                ОбъектТовар.ОсновнойПоставщик = СсылкаПоставщик;
                
                // Безопасное определение категории
                Попытка
                    ОбъектТовар.Категория = Перечисления.Категории[СтрКатегория];
                Исключение
                    // Идентификатор не найден среди значений перечисления
                КонецПопытки;
                
                ОбъектТовар.Записать();
                ЗагруженоТоваров = ЗагруженоТоваров + 1;
            КонецЕсли;
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение "Ошибка при импорте данных: " + ОписаниеОшибки();
    КонецПопытки;
    
    Возврат СтрШаблон("Успешно загружено товаров: %1. Пропущено некорректных строк: %2.", ЗагруженоТоваров, Ошибок);
КонецФункции

Рекомендации по промышленной эксплуатации механизмов импорта

Для предотвращения сбоев при регулярном обмене данными рекомендуется внедрять следующие правила проектирования:

← На главную