Загрузка данных из внешних источников, таких как Excel, — это классическая задача для разработчика 1С. Однако на пути к ее реализации часто возникают трудности, когда визуально идентичные строки не сопоставляются программой. В этой статье мы подробно разберем ситуацию, когда метод НайтиПоНаименованию возвращает пустую ссылку, хотя искомый элемент точно присутствует в базе. Мы проанализируем технические нюансы, начиная от ограничений метаданных и заканчивая «невидимыми» символами форматирования.
Рассмотрим типичный фрагмент кода, который используется для чтения данных из Excel через ТабличныйДокумент и последующего поиска транспортного средства (ТС) в справочнике:
Для НомерСтроки = 6 По КоличесвтоСтрок Цикл
// Читаем текст из второй колонки (наименование ТС)
ТСИзТабДок = ТабДок.Область(НомерСтроки, 2, НомерСтроки, 2).Текст;
// Пытаемся найти ссылку в справочнике
ТС = Справочники.ТС.НайтиПоНаименованию(ТСИзТабДок);
Если ТС.Пустая() Тогда
Сообщить("Элемент не найден: " + ТСИзТабДок);
КонецЕсли;
КонецЦикла;
Проблема заключается в том, что даже если в переменной ТСИзТабДок находится строка, которая «на глаз» совпадает с названием в 1С, метод поиска может не сработать. Разберем по шагам причины этого поведения.
Выясним самую коварную причину, с которой столкнулся автор темы. В системе 1С каждый реквизит справочника, включая стандартное Наименование, имеет фиксированную длину (например, 25, 50 или 100 символов). Рассмотрим, что происходит при несовпадении длин:
НайтиПоНаименованию пытается найти точное соответствие полной строки из 30 символов. Так как в базе физически нет строки длиннее 25 символов, поиск завершается неудачей.Решение: Проверим длину реквизита Наименование в конфигураторе. Если она меньше, чем данные в файле, необходимо либо увеличить длину в метаданных, либо обрезать искомую строку функцией Лев(СтрокаИзExcel, МаксДлина) перед поиском.
Проанализируем структуру текста, приходящего из Excel. Часто ячейки содержат не только буквы, но и спецсимволы, такие как неразрывный пробел (код 160). Обычная функция СокрЛП удаляет только стандартные пробелы (код 32). Если в конце строки из файла затесался неразрывный пробел, визуально в отладчике строки будут одинаковыми, но для системы это разные значения.
Для очистки данных воспользуемся следующим подходом:
// Удаляем стандартные пробелы и неразрывные пробелы
ЧистоеНаименование = СокрЛП(СтрЗаменить(ТСИзТабДок, Символы.НПП, ""));
ТС = Справочники.ТС.НайтиПоНаименованию(ЧистоеНаименование);
При работе с объектом ТабличныйДокумент важно понимать разницу между свойствами области ячеек. Посмотрим на них внимательнее:
Область.Текст — возвращает строковое представление. Если в Excel число 123 отформатировано как "123,00", свойство вернет именно строку с запятой и нулями.Область.Значение — возвращает типизированные данные (Число, Дата, Строка), если ячейка была прочитана с сохранением типа.Если поиск не срабатывает, попробуем изменить логику получения данных, как предложено в обсуждении:
Обл = ТабДок.Область(НомерСтроки, 2, НомерСтроки, 2);
Если Обл.СодержитЗначение Тогда
ТСИзТабДок = СокрЛП(Строка(Обл.Значение));
Иначе
ТСИзТабДок = СокрЛП(Обл.Текст);
КонецЕсли;
Метод НайтиПоНаименованию имеет второй параметр — ТочноеСоответствие. По умолчанию он равен Ложь. Это означает, что 1С ищет элемент, наименование которого начинается с искомой строки.
Важный нюанс: Если в базе есть элементы "Газель" и "Газель 123", то при поиске по строке "Газель" с параметром Ложь система вернет первый попавшийся элемент. Если же установить Истина, поиск будет строгим. Всегда используйте Истина, если вам нужно найти конкретный объект, а не похожий по началу названия.
Разберем ситуацию с точки зрения производительности. Если в файле Excel тысячи строк, вызов НайтиПоНаименованию в цикле создаст огромную нагрузку на базу данных, так как каждый вызов — это отдельный запрос к серверу SQL.
Профессиональный подход заключается в использовании запроса с временной таблицей или предварительной выгрузкой справочника в объект Соответствие. Рассмотрим пример с использованием Соответствие:
// 1. Сначала подготовим кэш всех наименований ТС
КэшТС = Новый Соответствие;
Выборка = Справочники.ТС.Выбрать();
Пока Выборка.Следующий() Цикл
// Ключ - наименование в верхнем регистре (для исключения проблем с регистром), значение - ссылка
КэшТС.Вставить(ВРег(Выборка.Наименование), Выборка.Ссылка);
КонецЦикла;
// 2. В цикле загрузки просто берем данные из памяти
Для НомерСтроки = 6 По КоличесвтоСтрок Цикл
НаименованиеИзФайла = СокрЛП(ТабДок.Область(НомерСтроки, 2).Текст);
СсылкаНаТС = КэшТС.Получить(ВРег(НаименованиеИзФайла));
Если СсылкаНаТС = Неопределено Тогда
// Обработка ситуации, когда в справочнике нет такого ТС
КонецЕсли;
КонецЦикла;
Этот метод работает в десятки раз быстрее, так как обращение к базе данных происходит всего один раз в самом начале.
Для успешного поиска элементов при импорте из внешних систем всегда следуйте этим правилам:
СокрЛП и удаляйте неразрывные пробелы через СтрЗаменить.ВРег для искомой строки и данных в базе, чтобы избежать проблем с разным написанием (например, "тс" и "ТС").СтрДлина) для обоих сравниваемых значений — это сразу покажет наличие скрытых символов.Следуя этим шагам, мы сможем построить надежный механизм интеграции, устойчивый к ошибкам форматирования в исходных файлах.