При интеграции 1С с сайтами и маркетплейсами часто возникает задача автоматического заполнения текстового описания товара из прикрепленных файлов описаний. В конфигурациях, таких как 1С:Управление торговлей 11, 1С:ERP или 1С:Комплексная автоматизация, подробные характеристики номенклатуры нередко хранятся в формате HTML внутри справочника присоединенных файлов. Если попытаться перенести их в строковый реквизит напрямую, мы сталкиваемся с разметкой тегов, потерей структуры таблиц или появлением невидимых управляющих символов. Разберем по шагам, как правильно извлечь файл из подсистемы БСП, корректно распарсить HTML-содержимое в читаемый текст и устранить сопутствующие артефакты.
Проанализируем распространенную ошибку: попытку вручную склеить путь к файлу на диске из реквизитов ПутьКФайлу и настроек каталогов томов. В типовых решениях файлы могут храниться как в томах на сервере, так и непосредственно в информационной базе. Конфигурация уже содержит готовые механизмы подсистемы «Работа с файлами», которые полностью изолируют разработчика от низкоуровневых настроек хранения.
Для гарантированного получения файла используем экспортные методы БСП. Посмотрим на пример кода функции, которая находит нужный файл номенклатуры и считывает его текст:
Функция ПолучитьТекстHTMLФайла(НоменклатураСсылка) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Файлы.Ссылка КАК Ссылка
|ИЗ
| Справочник.НоменклатураПрисоединенныеФайлы КАК Файлы
|ГДЕ
| Файлы.ВладелецФайла = &Номенклатура
| И Файлы.Расширение = &Расширение
| И НЕ Файлы.ПометкаУдаления";
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);
Запрос.УстановитьПараметр("Расширение", "html");
Выборка = Запрос.Выполнить().Выбрать();
Если Не Выборка.Следующий() Тогда
Возврат "";
КонецЕсли;
// В БСП извлеченный текст может уже храниться в реквизите ТекстХранилище
ФайлОбъект = Выборка.Ссылка.ПолучитьОбъект();
Если ФайлОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекста.Извлечен Тогда
ИзвлеченныйТекст = ФайлОбъект.ТекстХранилище.Получить();
Если Не ПустаяСтрока(ИзвлеченныйТекст) Тогда
Возврат ИзвлеченныйТекст;
КонецЕсли;
КонецЕсли;
// Получаем двоичные данные файла универсальным методом БСП
ДвоичныеДанные = РаботаСФайлами.ДвоичныеДанныеФайла(Выборка.Ссылка);
// Преобразуем двоичные данные в строку HTML
Возврат ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, КодировкаТекста.UTF8);
КонецФункции
Объект платформы ИзвлечениеТекста в среде Windows опирается на системные библиотеки фильтрации IFilter. Это приводит к двум существенным проблемам:
<td> и <tr> без добавления переводов строк, в результате чего структурированная спецификация товара превращается в одну непрерывную строчку.Чтобы сохранить переносы строк и структуру параметров, рассмотрим более надежные способы конвертации.
Наиболее производительным и кроссплатформенным решением является последовательное чтение документа с помощью ЧтениеHTML. Мы можем явно отслеживать теги строк и ячеек, вставляя нужные разделители.
Разберем реализацию такого алгоритма:
Функция ПреобразоватьHTMLВТекстЧтением(ИсходныйHTML) Экспорт
ЧтениеHTML = Новый ЧтениеHTML;
ЧтениеHTML.УстановитьСтроку(ИсходныйHTML);
Результат = Новый ТекстовыйДокумент;
ТекущаяСтрока = "";
Пока ЧтениеHTML.Прочитать() Цикл
ТипУзла = ЧтениеHTML.ТипУзла;
ИмяУзла = НРег(ЧтениеHTML.Имя);
Если ТипУзла = ТипУзлаHTML.НачалоЭлемента Тогда
Если ИмяУзла = "tr" Или ИмяУзла = "p" Или ИмяУзла = "div" Или ИмяУзла = "br" Тогда
Если Не ПустаяСтрока(СокрЛП(ТекущаяСтрока)) Тогда
Результат.ДобавитьСтроку(СокрЛП(ТекущаяСтрока));
ТекущаяСтрока = "";
КонецЕсли;
ИначеЕсли ИмяУзла = "td" Или ИмяУзла = "th" Тогда
Если Не ПустаяСтрока(ТекущаяСтрока) Тогда
ТекущаяСтрока = ТекущаяСтрока + " ";
КонецЕсли;
КонецЕсли;
ИначеЕсли ТипУзла = ТипУзлаHTML.Текст Тогда
ТекстУзла = ЧтениеHTML.Значение;
// Устраняем лишние внутренние переносы узлов
ТекстУзла = СтрЗаменить(ТекстУзла, Символы.ПС, "");
ТекстУзла = СтрЗаменить(ТекстУзла, Символы.ВК, "");
ТекущаяСтрока = ТекущаяСтрока + ТекстУзла;
ИначеЕсли ТипУзла = ТипУзлаHTML.КонецЭлемента Тогда
Если ИмяУзла = "tr" Или ИмяУзла = "p" Тогда
Если Не ПустаяСтрока(СокрЛП(ТекущаяСтрока)) Тогда
Результат.ДобавитьСтроку(СокрЛП(ТекущаяСтрока));
ТекущаяСтрока = "";
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если Не ПустаяСтрока(СокрЛП(ТекущаяСтрока)) Тогда
Результат.ДобавитьСтроку(СокрЛП(ТекущаяСтрока));
КонецЕсли;
Возврат Результат.ПолучитьТекст();
КонецФункции
Если требуется выстроить форматирование вида «Параметр: Значение», эффективнее разобрать документ в объектную модель и обойти строки таблицы:
Функция ПреобразоватьТаблицуDOM(ИсходныйHTML) Экспорт
ЧтениеHTML = Новый ЧтениеHTML;
ЧтениеHTML.УстановитьСтроку(ИсходныйHTML);
Построитель = Новый ПостроительDOM;
ДокументHTML = Построитель.Прочитать(ЧтениеHTML);
СтрокиТаблицы = ДокументHTML.ПолучитьЭлементыПоИмениДляМассива("tr");
Результат = Новый Массив;
Для Каждого СтрокаTR Из СтрокиТаблицы Цикл
ЯчейкиTD = СтрокаTR.ПолучитьЭлементыПоИмениДляМассива("td");
Если ЯчейкиTD.Количество() = 2 Тогда
Результат.Добавить(СокрЛП(ЯчейкиTD[0].ТекстовоеСодержимое) + ": " + СокрЛП(ЯчейкиTD[1].ТекстовоеСодержимое));
ИначеЕсли ЯчейкиTD.Количество() = 1 Тогда
Результат.Добавить(СокрЛП(ЯчейкиTD[0].ТекстовоеСодержимое));
КонецЕсли;
КонецЦикла;
Возврат СтрСоединить(Результат, Символы.ПС);
КонецФункции
Выясним причину появления странных квадратиков и знаков вопроса в начале строки или искажений разделителей при обработке текста.
U+FEFF (код символа 65279). Внешние редакторы и функции конвертации могут искажать его в вопросительный знак (код 63) или управляющий блок. Для его надежного удаления выполним проверку:
Если СтрДлина(Текст) > 0 И КодСимвола(Текст, 1) = 65279 Тогда
Текст = Сред(Текст, 2);
КонецЕсли;
. При декодировании в строку 1С они превращаются в символ с кодом 160. Платформа предоставляет предопределенное значение Символы.НПП. Заменим их на классический пробел:
Текст = СтрЗаменить(Текст, Символы.НПП, " ");
Символы.Таб не создает ровную визуальную колонку, а выглядит как обычный пробел переменной ширины. Наиболее устойчивый способ оформления табличных характеристик в обычном многострочном поле ввода — формат «Имя параметра: Значение».
Соберем все преобразования в финальную процедуру, которую можно вызывать перед записью номенклатуры:
Процедура ЗаполнитьТекстовоеОписание(НоменклатураОбъект) Экспорт
ТекстHTML = ПолучитьТекстHTMLФайла(НоменклатураОбъект.Ссылка);
Если ПустаяСтрока(ТекстHTML) Тогда
Возврат;
КонецЕсли;
ОбработанныйТекст = ПреобразоватьHTMLВТекстЧтением(ТекстHTML);
// Удаляем маркер последовательности байтов BOM
Если СтрДлина(ОбработанныйТекст) > 0 И КодСимвола(ОбработанныйТекст, 1) = 65279 Тогда
ОбработанныйТекст = Сред(ОбработанныйТекст, 2);
КонецЕсли;
// Устраняем неразрывные пробелы
ОбработанныйТекст = СтрЗаменить(ОбработанныйТекст, Символы.НПП, " ");
// Записываем результат с ограничением длины строкового реквизита
НоменклатураОбъект.Описание = Лев(СокрЛП(ОбработанныйТекст), 1024);
КонецПроцедуры
Такой подход обеспечивает корректное извлечение данных вне зависимости от настроек томов хранения БСП, корректно формирует переносы строк для таблиц и очищает результирующее описание от системных непечатных символов.