Как в 1С преобразовать присоединенный HTML-файл в обычный текст с сохранением структуры таблиц?

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

При интеграции 1С с сайтами и маркетплейсами часто возникает задача автоматического заполнения текстового описания товара из прикрепленных файлов описаний. В конфигурациях, таких как 1С:Управление торговлей 11, 1С:ERP или 1С:Комплексная автоматизация, подробные характеристики номенклатуры нередко хранятся в формате HTML внутри справочника присоединенных файлов. Если попытаться перенести их в строковый реквизит напрямую, мы сталкиваемся с разметкой тегов, потерей структуры таблиц или появлением невидимых управляющих символов. Разберем по шагам, как правильно извлечь файл из подсистемы БСП, корректно распарсить HTML-содержимое в читаемый текст и устранить сопутствующие артефакты.

Шаг 1. Получение содержимого присоединенного файла средствами БСП

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

Для гарантированного получения файла используем экспортные методы БСП. Посмотрим на пример кода функции, которая находит нужный файл номенклатуры и считывает его текст:


Функция ПолучитьТекстHTMLФайла(НоменклатураСсылка) Экспорт

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

    // В БСП извлеченный текст может уже храниться в реквизите ТекстХранилище
    ФайлОбъект = Выборка.Ссылка.ПолучитьОбъект();
    Если ФайлОбъект.СтатусИзвлеченияТекста = Перечисления.СтатусыИзвлеченияТекста.Извлечен Тогда
        ИзвлеченныйТекст = ФайлОбъект.ТекстХранилище.Получить();
        Если Не ПустаяСтрока(ИзвлеченныйТекст) Тогда
            Возврат ИзвлеченныйТекст;
        КонецЕсли;
    КонецЕсли;

    // Получаем двоичные данные файла универсальным методом БСП
    ДвоичныеДанные = РаботаСФайлами.ДвоичныеДанныеФайла(Выборка.Ссылка);
    
    // Преобразуем двоичные данные в строку HTML
    Возврат ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, КодировкаТекста.UTF8);

КонецФункции

Шаг 2. Почему платформенный объект «ИзвлечениеТекста» не всегда подходит

Объект платформы ИзвлечениеТекста в среде Windows опирается на системные библиотеки фильтрации IFilter. Это приводит к двум существенным проблемам:

  1. На серверных операционных системах компонент для разбора HTML может отсутствовать либо быть отключен, из-за чего возникает исключение «Ошибка извлечения текста из файла».
  2. Стандартный механизм склеивает содержимое тегов таблиц <td> и <tr> без добавления переводов строк, в результате чего структурированная спецификация товара превращается в одну непрерывную строчку.

Чтобы сохранить переносы строк и структуру параметров, рассмотрим более надежные способы конвертации.

Способ 1. Парсинг разметки через объект «ЧтениеHTML» (рекомендуемый)

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

Разберем реализацию такого алгоритма:


Функция ПреобразоватьHTMLВТекстЧтением(ИсходныйHTML) Экспорт

    ЧтениеHTML = Новый ЧтениеHTML;
    ЧтениеHTML.УстановитьСтроку(ИсходныйHTML);
    
    Результат = Новый ТекстовыйДокумент;
    ТекущаяСтрока = "";
    
    Пока ЧтениеHTML.Прочитать() Цикл
        
        ТипУзла = ЧтениеHTML.ТипУзла;
        ИмяУзла = НРег(ЧтениеHTML.Имя);
        
        Если ТипУзла = ТипУзлаHTML.НачалоЭлемента Тогда
            
            Если ИмяУзла = "tr" Или ИмяУзла = "p" Или ИмяУзла = "div" Или ИмяУзла = "br" Тогда
                Если Не ПустаяСтрока(СокрЛП(ТекущаяСтрока)) Тогда
                    Результат.ДобавитьСтроку(СокрЛП(ТекущаяСтрока));
                    ТекущаяСтрока = "";
                КонецЕсли;
            ИначеЕсли ИмяУзла = "td" Или ИмяУзла = "th" Тогда
                Если Не ПустаяСтрока(ТекущаяСтрока) Тогда
                    ТекущаяСтрока = ТекущаяСтрока + "  ";
                КонецЕсли;
            КонецЕсли;
            
        ИначеЕсли ТипУзла = ТипУзлаHTML.Текст Тогда
            
            ТекстУзла = ЧтениеHTML.Значение;
            // Устраняем лишние внутренние переносы узлов
            ТекстУзла = СтрЗаменить(ТекстУзла, Символы.ПС, "");
            ТекстУзла = СтрЗаменить(ТекстУзла, Символы.ВК, "");
            ТекущаяСтрока = ТекущаяСтрока + ТекстУзла;
            
        ИначеЕсли ТипУзла = ТипУзлаHTML.КонецЭлемента Тогда
            
            Если ИмяУзла = "tr" Или ИмяУзла = "p" Тогда
                Если Не ПустаяСтрока(СокрЛП(ТекущаяСтрока)) Тогда
                    Результат.ДобавитьСтроку(СокрЛП(ТекущаяСтрока));
                    ТекущаяСтрока = "";
                КонецЕсли;
            КонецЕсли;
            
        КонецЕсли;
        
    КонецЦикла;
    
    Если Не ПустаяСтрока(СокрЛП(ТекущаяСтрока)) Тогда
        Результат.ДобавитьСтроку(СокрЛП(ТекущаяСтрока));
    КонецЕсли;
    
    Возврат Результат.ПолучитьТекст();

КонецФункции

Способ 2. Выборка узлов через «ПостроительDOM»

Если требуется выстроить форматирование вида «Параметр: Значение», эффективнее разобрать документ в объектную модель и обойти строки таблицы:


Функция ПреобразоватьТаблицу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].ТекстовоеСодержимое));
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат СтрСоединить(Результат, Символы.ПС);

КонецФункции

Шаг 3. Очистка текста от управляющих символов и артефактов

Выясним причину появления странных квадратиков и знаков вопроса в начале строки или искажений разделителей при обработке текста.

  1. Невидимый квадратик или знак вопроса в начале (байт-метка BOM):
    При чтении файлов в кодировке UTF-8 с меткой порядка байтов первым символом оказывается U+FEFF (код символа 65279). Внешние редакторы и функции конвертации могут искажать его в вопросительный знак (код 63) или управляющий блок. Для его надежного удаления выполним проверку:
    
    Если СтрДлина(Текст) > 0 И КодСимвола(Текст, 1) = 65279 Тогда
        Текст = Сред(Текст, 2);
    КонецЕсли;
    
  2. Неразрывные пробелы:
    В HTML пробелы внутри таблиц часто кодируются сущностью &nbsp;. При декодировании в строку 1С они превращаются в символ с кодом 160. Платформа предоставляет предопределенное значение Символы.НПП. Заменим их на классический пробел: Текст = СтрЗаменить(Текст, Символы.НПП, " ");
  3. Выравнивание колонок с помощью знака табуляции:
    Элементы управления тонкого клиента 1С отображают текст с использованием пропорциональных шрифтов. По этой причине Символы.Таб не создает ровную визуальную колонку, а выглядит как обычный пробел переменной ширины. Наиболее устойчивый способ оформления табличных характеристик в обычном многострочном поле ввода — формат «Имя параметра: Значение».

Итоговая функция обработки текста

Соберем все преобразования в финальную процедуру, которую можно вызывать перед записью номенклатуры:


Процедура ЗаполнитьТекстовоеОписание(НоменклатураОбъект) Экспорт

    ТекстHTML = ПолучитьТекстHTMLФайла(НоменклатураОбъект.Ссылка);
    Если ПустаяСтрока(ТекстHTML) Тогда
        Возврат;
    КонецЕсли;

    ОбработанныйТекст = ПреобразоватьHTMLВТекстЧтением(ТекстHTML);
    
    // Удаляем маркер последовательности байтов BOM
    Если СтрДлина(ОбработанныйТекст) > 0 И КодСимвола(ОбработанныйТекст, 1) = 65279 Тогда
        ОбработанныйТекст = Сред(ОбработанныйТекст, 2);
    КонецЕсли;
    
    // Устраняем неразрывные пробелы
    ОбработанныйТекст = СтрЗаменить(ОбработанныйТекст, Символы.НПП, " ");
    
    // Записываем результат с ограничением длины строкового реквизита
    НоменклатураОбъект.Описание = Лев(СокрЛП(ОбработанныйТекст), 1024);

КонецПроцедуры

Такой подход обеспечивает корректное извлечение данных вне зависимости от настроек томов хранения БСП, корректно формирует переносы строк для таблиц и очищает результирующее описание от системных непечатных символов.

← На главную