При работе с 1С программисты часто сталкиваются с необходимостью динамического формирования табличных данных. Один из распространенных сценариев – это создание колонок в объекте ТаблицаЗначений на основе данных, полученных из массива. Однако, при попытке добавить строку из массива напрямую в качестве имени колонки, мы можем столкнуться с ошибкой "Неверное имя колонки". Давайте разберем, почему возникает эта ошибка и как ее можно эффективно решить.
Когда мы пытаемся добавить колонку в ТаблицуЗначений, используя метод Колонки.Добавить(), 1С предъявляет строгие требования к имени этой колонки. Имя колонки должно соответствовать правилам именования идентификаторов в языке 1С, что означает:
В нашем случае, когда мы получаем значения из массива, эти значения могут быть произвольными строками, например, " Наименование элемента ", "2-ой Показатель", "Количество (шт.)". Даже после использования функции СокрЛП(), которая убирает пробелы в начале и конце строки, внутри строки могут остаться недопустимые символы, или строка может начинаться с цифры, что приведет к ошибке.
Давайте посмотрим на пример кода, который вызывает ошибку:
ТЗ1 = Новый ТаблицаЗначений;
Массив1 = СтрРазделить(РеквизитТаблицы,",",Ложь); // Пример: РеквизитТаблицы = " Наименование1 ,Количество (шт.), 2 Показатель"
Для Индекс = 0 По Массив1.ВГраница Цикл
стр = СокрЛП(Массив1[Индекс]); // Убираем начальные/конечные пробелы
Попытка
ТЗ1.Колонки.Добавить(стр); // Здесь может возникнуть ошибка
Исключение
Сообщить("Ошибка при добавлении колонки '" + стр + "': " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
В этом примере, если стр примет значение, например, "Количество (шт.)" или "2 Показатель", 1С выдаст ошибку "Неверное имя колонки", потому что скобки, пробелы внутри строки и начальная цифра не соответствуют правилам.
Это один из самых надежных и рекомендуемых способов обработки динамических имен колонок. Мы создаем колонки со служебными, заведомо корректными именами (например, "Колонка1", "Колонка2", "Данные_XXX"), а исходное значение из массива, которое может содержать любые символы, присваиваем свойству Заголовок у созданной колонки. Свойство Заголовок предназначено для отображения пользователю и не имеет таких строгих ограничений на символы.
Рассмотрим по шагам, как реализовать этот подход:
Заголовок созданной колонки.
Посмотрим на пример кода:
ТЗ1 = Новый ТаблицаЗначений;
Массив1 = СтрРазделить(РеквизитТаблицы,",",Ложь); // Пример: " Наименование1 ,Количество (шт.), 2 Показатель"
// Создаем Соответствие для хранения маппинга: ИсходноеНазвание -> СлужебноеИмяКолонки
СоответствиеИменКолонок = Новый Соответствие;
ИндексКолонки = 1;
Для Каждого СтрокаМассива Из Массив1 Цикл
ОригинальноеИмя = СокрЛП(СтрокаМассива);
// Генерируем служебное имя, которое гарантированно будет корректным
СлужебноеИмяКолонки = "Колонка_" + ИндексКолонки;
// Добавляем колонку
НоваяКолонка = ТЗ1.Колонки.Добавить(СлужебноеИмяКолонки);
// Присваиваем оригинальное имя в Заголовок
НоваяКолонка.Заголовок = ОригинальноеИмя;
// Опционально: устанавливаем тип данных для колонки
// Если тип не указан, колонка будет иметь неопределенный тип
НоваяКолонка.Тип = Новый ОписаниеТипов("Строка");
// Сохраняем соответствие
СоответствиеИменКолонок.Вставить(ОригинальноеИмя, СлужебноеИмяКолонки);
ИндексКолонки = ИндексКолонки + 1;
КонецЦикла;
// Теперь мы можем добавлять строки и заполнять данные
// Например, если у нас есть данные для "Количество (шт.)"
НоваяСтрока = ТЗ1.Добавить();
// Получаем служебное имя для "Количество (шт.)"
СлужебноеИмяДляКоличества = СоответствиеИменКолонок.Получить("Количество (шт.)");
Если СлужебноеИмяДляКоличества <> Неопределено Тогда
НоваяСтрока[СлужебноеИмяДляКоличества] = 123;
КонецЕсли;
// Для отображения пользователю ТЗ будет выглядеть с заголовками "Наименование1", "Количество (шт.)" и т.д.
// А для программного доступа мы используем СлужебноеИмяКолонки.
// Отладка:
Сообщить("Структура ТЗ:");
Для Каждого Кол Из ТЗ1.Колонки Цикл
Сообщить("Имя: " + Кол.Имя + ", Заголовок: " + Кол.Заголовок + ", Тип: " + Кол.Тип);
КонецЦикла;
Этот подход гарантирует работоспособность в любых условиях, независимо от содержимого исходных строк. Единственное "усложнение" – это необходимость использовать Соответствие для маппинга и обращаться к колонкам по служебным именам. Однако это повышает надежность и стабильность кода.
Иногда нам все же требуется использовать максимально похожее на исходное имя в качестве имени колонки, без использования заголовков. В этом случае нам необходимо преобразовать исходную строку так, чтобы она соответствовала правилам именования идентификаторов 1С. Это означает удаление или замену всех недопустимых символов.
Рассмотрим логику такого преобразования:
СокрЛП().
_.
"Колонка_".
В Библиотеке стандартных подсистем (БСП) есть функция ПреобразоватьСтрокуВДопустимоеНаименованиеКолонки, которая реализует подобную логику. Если у нас нет доступа к БСП или требуется специфичная логика, мы можем создать свою функцию:
// Функция для преобразования строки в допустимое имя колонки
Функция ПолучитьДопустимоеИмяКолонки(ИсходнаяСтрока) Экспорт
ИмяКолонки = СокрЛП(ИсходнаяСтрока);
// Заменяем все недопустимые символы на '_'
// Можно перечислить символы, которые хотим заменить
ИмяКолонки = СтрЗаменить(ИмяКолонки, " ", "_");
ИмяКолонки = СтрЗаменить(ИмяКолонки, "(", "_");
ИмяКолонки = СтрЗаменить(ИмяКолонки, ")", "_");
ИмяКолонки = СтрЗаменить(ИмяКолонки, "-", "_");
ИмяКолонки = СтрЗаменить(ИмяКолонки, ".", "_");
ИмяКолонки = СтрЗаменить(ИмяКолонки, ",", "_");
ИмяКолонки = СтрЗаменить(ИмяКолонки, "/", "_");
ИмяКолонки = СтрЗаменить(ИмяКолонки, "\", "_");
// Добавьте другие символы по необходимости
// Удаляем повторяющиеся подчеркивания (опционально, для читаемости)
Пока Найти(ИмяКолонки, "__") > 0 Цикл
ИмяКолонки = СтрЗаменить(ИмяКолонки, "__", "_");
КонецЦикла;
// Проверяем, не начинается ли имя с цифры
Если Лев(ИмяКолонки, 1) = "0" ИЛИ Лев(ИмяКолонки, 1) = "1" ИЛИ
Лев(ИмяКолонки, 1) = "2" ИЛИ Лев(ИмяКолонки, 1) = "3" ИЛИ
Лев(ИмяКолонки, 1) = "4" ИЛИ Лев(ИмяКолонки, 1) = "5" ИЛИ
Лев(ИмяКолонки, 1) = "6" ИЛИ Лев(ИмяКолонки, 1) = "7" ИЛИ
Лев(ИмяКолонки, 1) = "8" ИЛИ Лев(ИмяКолонки, 1) = "9" Тогда
ИмяКолонки = "Колонка_" + ИмяКолонки;
КонецЕсли;
// Если имя стало пустым или слишком коротким после преобразований
Если СтрДлина(ИмяКолонки) = 0 Тогда
ИмяКолонки = "ПустаяКолонка"; // Или генерируем уникальное имя
КонецЕсли;
Возврат ИмяКолонки;
КонецФункции;
ТЗ1 = Новый ТаблицаЗначений;
Массив1 = СтрРазделить(РеквизитТаблицы,",",Ложь); // Пример: " Наименование1 ,Количество (шт.), 2 Показатель"
Для Каждого СтрокаМассива Из Массив1 Цикл
ОригинальноеИмя = СокрЛП(СтрокаМассива);
ДопустимоеИмя = ПолучитьДопустимоеИмяКолонки(ОригинальноеИмя);
Попытка
ТЗ1.Колонки.Добавить(ДопустимоеИмя);
Исключение
Сообщить("Ошибка при добавлении колонки '" + ДопустимоеИмя + "' (оригинал: '" + ОригинальноеИмя + "'): " + ОписаниеОшибки());
КонецПопытки;
КонецЦикла;
Этот метод подходит, когда нам важно, чтобы имя колонки было максимально близко к исходному значению, но при этом было синтаксически корректным. Однако мы должны быть готовы к тому, что преобразованное имя может отличаться от исходного, и пользователю может быть не сразу понятно, к какому столбцу оно относится. В этом случае, даже при использовании преобразования, мы можем дополнительно использовать свойство Заголовок для сохранения оригинального наименования.
В некоторых случаях, когда нам нужно хранить очень динамичные данные, и жесткое именование колонок по каждому элементу массива вызывает сложности, стоит рассмотреть альтернативные структуры данных. Объект ТаблицаЗначений отлично подходит для табличных данных с предопределенным или предсказуемым набором колонок. Однако для более свободных структур могут быть более подходящие варианты.
Массив, где каждый элемент массива будет Структурой. Ключи структуры будут выступать в роли "имен колонок", а их значения – это данные. Этот подход особенно удобен, если количество и имена "колонок" могут сильно варьироваться для каждой "строки".
МассивДанных = Новый Массив;
// Первая "строка" данных
СтруктураДанных1 = Новый Структура;
СтруктураДанных1.Вставить("НаименованиеТовара", "Монитор");
СтруктураДанных1.Вставить("Цена", 15000);
МассивДанных.Добавить(СтруктураДанных1);
// Вторая "строка" данных с другим набором полей
СтруктураДанных2 = Новый Структура;
СтруктураДанных2.Вставить("Код", "00123");
СтруктураДанных2.Вставить("КоличествоНаСкладе", 100);
СтруктураДанных2.Вставить("Поставщик", "Рога и Копыта");
МассивДанных.Добавить(СтруктураДанных2);
// Обработка данных
Для Каждого ДанныеСтроки Из МассивДанных Цикл
Если ДанныеСтроки.Свойство("НаименованиеТовара") Тогда
Сообщить("Товар: " + ДанныеСтроки.НаименованиеТовара);
КонецЕсли;
Если ДанныеСтроки.Свойство("КоличествоНаСкладе") Тогда
Сообщить("На складе: " + ДанныеСтроки.КоличествоНаСкладе);
КонецЕсли;
КонецЦикла;
ТаблицуЗначений с предопределенными колонками, например, "Показатель" и "Значение". Каждая строка такой таблицы будет представлять собой отдельный "показатель" с его значением. Этот подход известен как "сворачивание" или "транспонирование" данных.
ТЗПоказателей = Новый ТаблицаЗначений;
ТЗПоказателей.Колонки.Добавить("Показатель", Новый ОписаниеТипов("Строка"));
ТЗПоказателей.Колонки.Добавить("Значение", Новый ОписаниеТипов("Число"));
// Добавляем данные
НоваяСтрока = ТЗПоказателей.Добавить();
НоваяСтрока.Показатель = "Наименование элемента";
НоваяСтрока.Значение = "Какой-то текст"; // Здесь может быть любое значение, в зависимости от типа колонки
НоваяСтрока = ТЗПоказателей.Добавить();
НоваяСтрока.Показатель = "Количество (шт.)";
НоваяСтрока.Значение = 150;
НоваяСтрока = ТЗПоказателей.Добавить();
НоваяСтрока.Показатель = "2 Показатель";
НоваяСтрока.Значение = 25.5;
// Обработка данных
Для Каждого СтрокаТЗ Из ТЗПоказателей Цикл
Сообщить("Показатель: " + СтрокаТЗ.Показатель + ", Значение: " + СтрокаТЗ.Значение);
КонецЦикла;
Этот метод позволяет избежать динамического создания колонок, но требует иной логики обработки и представления данных, особенно если нужно отображать их в виде таблицы с несколькими столбцами.
При работе с динамическим созданием колонок в ТаблицеЗначений, особенно полезно учитывать следующие моменты:
Новый ОписаниеТипов(). Это повышает надежность кода и предотвращает потенциальные ошибки при дальнейшей работе с данными. Если тип не указан, по умолчанию колонка будет иметь неопределенный тип, что позволит хранить в ней значения любого типа, но может привести к проблемам с сортировкой или фильтрацией.
НоваяКолонка = ТЗ1.Колонки.Добавить("МояКолонка");
НоваяКолонка.Тип = Новый ОписаниеТипов("Строка"); // Указываем тип Строка
// Или для числа:
// НоваяКолонка.Тип = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 2));
ТаблицаЗначений: Помним, что ТаблицаЗначений – это динамический объект, который хранится в оперативной памяти и существует только во время выполнения программного кода. Для сохранения данных в базу данных необходимо использовать другие объекты, такие как табличные части документов, справочников или регистры.
ТЗ.Колонки.Добавить(ИмяКолонки). В режиме отладчика внимательно проверьте содержимое переменной ИмяКолонки (в нашем случае стр или ДопустимоеИмя). Это поможет точно определить, какое именно значение вызывает ошибку и какие недопустимые символы в нем содержатся. Часто бывает, что кроме пробелов, есть другие неочевидные символы, например, символы новой строки или табуляции.
// ...
стр = СокрЛП(Массив1[Индекс]);
// Установите здесь точку останова и посмотрите значение переменной "стр"
ТЗ1.Колонки.Добавить(стр);
// ...
Даже если СокрЛП() используется, мы должны убедиться, что проблема не в других внутренних символах. Внимательный анализ значения переменной в отладчике – ключ к пониманию истинной причины ошибки.
Принимая во внимание эти подходы и рекомендации, мы сможем эффективно и надежно работать с динамическим добавлением колонок в ТаблицуЗначений в 1С, избегая распространенных ошибок и создавая более гибкие и устойчивые решения.