Как исправить ошибку «Индекс находится за границами массива» при переборе реквизитов табличной части?

Программист IT и автоматизация бизнеса
← На главную

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

Анализ проблемного кода: почему происходит сбой?

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

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


ат = 0;
Для Каждого Строка из Док[ТабЧасть.Имя] Цикл
    Для Каждого Рек из Строка Цикл
        ОЗ = ОЗ + "\" + имена[ат] + "=" + Рек + Символы.ПС;                    
        Если ат < ит тогда
            ат = ат + 1;
        КонецЕсли;
    КонецЦикла;
    ат = 0;
КонецЦикла;

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

  1. Несвоевременное обнуление счетчика: Переменная ат, используемая как индекс массива, должна инициализироваться значением 0 в начале каждой новой строки табличной части. Если оставить обнуление только в конце внешнего цикла, логика может нарушиться при определенных условиях ветвления.
  2. Логика инкремента: Даже если в массиве 3 элемента (индексы 0, 1, 2), условие Если ат < ит при ит = 3 позволит переменной ат принять значение 3. При следующей итерации обращение имена[3] вызовет исключение, так как максимальный индекс — 2.
  3. Состав коллекции строки: Это самая важная причина. Когда мы пишем Для Каждого Рек из Строка Цикл, итератор обходит не только те реквизиты, которые мы видим в конфигураторе, но и стандартные реквизиты (например, НомерСтроки). Если в метаданных табличной части 3 пользовательских реквизита, а в строке фактически 4 колонки (включая номер строки), цикл по строке сделает 4 итерации, в то время как наш массив имен содержит только 3 элемента. Это неизбежно приведет к выходу за границы массива.

Решение №1: Исправление логики счетчиков

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


Для Каждого ТекущаяСтрока Из Док[ТабЧасть.Имя] Цикл
    // Обнуляем индекс строго перед началом обхода колонок текущей строки
    ИндексРеквизита = 0; 
    
    Для Каждого ЗначениеКолонки Из ТекущаяСтрока Цикл
        // Проверяем, не превысили ли мы размер массива имен
        Если ИндексРеквизита < имена.Количество() Тогда
            Сообщить("Реквизит: " + имена[ИндексРеквизита] + " = " + ЗначениеКолонки);
            ИндексРеквизита = ИндексРеквизита + 1;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

Однако этот метод все еще остается "хрупким", так как он полагается на то, что порядок колонок в объекте СтрокаТабличнойЧасти совпадает с порядком в коллекции Метаданные.Реквизиты, что не всегда гарантировано.

Решение №2: Оптимальный подход через метаданные (Рекомендуется)

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

Рассмотрим пошагово правильный алгоритм:

  1. Обращаемся к метаданным табличной части документа.
  2. Перебираем строки табличной части.
  3. Внутри каждой строки запускаем цикл по коллекции реквизитов из метаданных.
  4. Получаем значение через обращение к строке по имени: Строка[ИмяРеквизита].

Пример реализации:


// Предположим, ТабЧастьМетаданные — это объект метаданных конкретной ТЧ
Для Каждого СтрокаТЧ Из Док[ТабЧастьМетаданные.Имя] Цикл
    
    НомерРеквизита = 0;
    
    // Итерируемся не по строке, а по описанию реквизитов в метаданных
    Для Каждого РеквизитМетаданные Из ТабЧастьМетаданные.Реквизиты Цикл
        
        НомерРеквизита = НомерРеквизита + 1;
        ИмяРеквизита = РеквизитМетаданные.Имя;
        Значение = СтрокаТЧ[ИмяРеквизита];
        
        // Формируем информационное сообщение
        ТекстСообщения = СтрШаблон("%1/%2 \ %3 = %4", 
                            НомерРеквизита, 
                            ТабЧастьМетаданные.Реквизиты.Количество(), 
                            РеквизитМетаданные.Синоним, 
                            Строка(Значение));
        Сообщить(ТекстСообщения);
        
    КонецЦикла;
    
КонецЦикла;

Преимущества этого метода: - Надежность: Вы никогда не выйдете за границы массива, так как количество итераций строго определено составом реквизитов. - Гибкость: Вы можете легко получить доступ к синониму, типу или другим свойствам реквизита через объект метаданных. - Читаемость: Код становится понятным и не перегружен техническими переменными-счетчиками.

Решение №3: Использование выгрузки в Таблицу Значений

Проанализируем еще один эффективный способ, который особенно полезен, если данных в табличной части много. Мы можем выгрузить табличную часть в объект ТаблицаЗначений. У этого объекта есть коллекция Колонки, которая ведет себя гораздо более предсказуемо, чем строка данных формы.


// Выгружаем ТЧ в таблицу значений
ТаблицаДанных = Док[ТабЧасть.Имя].Выгрузить();

Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
    Для Каждого Колонка Из ТаблицаДанных.Колонки Цикл
        // Здесь Колонка.Имя всегда соответствует значению в СтрокаДанных
        ИмяКолонки = Колонка.Имя;
        Значение = СтрокаДанных[ИмяКолонки];
        
        Сообщить("Колонка: " + ИмяКолонки + " Значение: " + Значение);
    КонецЦикла;
КонецЦикла;

Этот метод удобен тем, что в ТаблицаДанных.Колонки попадут только значимые данные, и вы сможете обращаться к ним программно без риска получить ошибку индексации.

Важные советы по предотвращению ошибок с массивами

Подводя итог, разберем основные правила безопасной работы с массивами и коллекциями в 1С:

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

← На главную