В процессе разработки на платформе 1С:Предприятие часто возникает задача динамического изменения текста, который уже имеет определенное оформление: выделение цветом, жирный шрифт или наклон. Типичная ошибка начинающего разработчика — попытка использовать стандартную функцию СтрЗаменить() напрямую к объекту типа ФорматированнаяСтрока. В этой статье мы подробно разберем, почему классический подход не работает и какими методами можно выполнить замену или удаление подстроки, сохранив при этом визуальный стиль сообщения.
Разберем ситуацию: у нас есть объект ФорматированнаяСтрока. При попытке выполнить следующий код оформление мгновенно пропадает:
МояСтрока = Новый ФорматированнаяСтрока("Важное сообщение", Новый Шрифт(,,Истина), ЦветаСтиля.ЦветОшибки);
// Пытаемся удалить слово "Важное"
МояСтрока = СтрЗаменить(МояСтрока, "Важное", "");
Проблема заключается в том, что функция СтрЗаменить() работает исключительно с типом Строка. Как только вы передаете в нее ФорматированнаяСтрока, платформа неявно преобразует объект в обычный текст, отбрасывая всю информацию о шрифтах и цветах. На выходе вы получаете простую строку, и повторное оборачивание в конструктор Новый ФорматированнаяСтрока() уже не вернет утраченные стили. Нам необходимо работать со структурой объекта, а не с его текстовым представлением.
Наиболее надежный и «официальный» способ манипуляции сложным текстом — использование объекта ФорматированныйДокумент. Этот объект специально спроектирован для работы с размеченным текстом и позволяет программно искать и изменять фрагменты.
Рассмотрим алгоритм действий по шагам:
ФорматированныйДокумент.ФорматированнуюСтроку.Проанализируем пример кода:
Док = Новый ФорматированныйДокумент;
Док.УстановитьФорматированнуюСтроку(ИсходнаяФорматированнаяСтрока);
// Ищем подстроку, которую нужно удалить
НайденныйИнтервал = Док.НайтиТекст(ПодстрокаДляУдаления);
Пока НайденныйИнтервал <> Неопределено Цикл
// Заменяем текст на пустую строку, сохраняя окружающее оформление
НайденныйИнтервал.ЗаменитьТекст("");
// Ищем следующее вхождение, если нужно удалить все
НайденныйИнтервал = Док.НайтиТекст(ПодстрокаДляУдаления);
КонецЦикла;
Результат = Док.ПолучитьФорматированнуюСтроку();
Этот метод хорош тем, что ФорматированныйДокумент берет на себя всю работу по сохранению тегов оформления внутри структуры документа.
Мало кто знает, что ФорматированнаяСтрока является коллекцией. Она состоит из элементов, которые могут быть либо обычными строками, либо вложенными объектами ФорматированнаяСтрока. Мы можем рекурсивно обойти эти элементы и применить замену только к текстовым частям.
Разберем, как реализовать такой обход:
Функция ОбработатьФорматированнуюСтроку(ИсходнаяСтрока, ЧтоИщем, НаЧтоМеняем)
МассивРезультат = Новый Массив;
Для Каждого Элемент Из ИсходнаяСтрока.Элементы Цикл
ТипЭлемента = ТипЗнч(Элемент);
Если ТипЭлемента = Тип("Строка") Тогда
// Если это просто текст, выполняем обычную замену
МассивРезультат.Добавить(СтрЗаменить(Элемент, ЧтоИщем, НаЧтоМеняем));
ИначеЕсли ТипЭлемента = Тип("ФорматированнаяСтрока") Тогда
// Если это вложенная строка со своим оформлением, обрабатываем ее свойства
// В реальной задаче здесь может потребоваться рекурсия или анализ свойств Font/Color
ТекстВнутри = СтрЗаменить(Строка(Элемент), ЧтоИщем, НаЧтоМеняем);
МассивРезультат.Добавить(Новый ФорматированнаяСтрока(ТекстВнутри, Элемент.Шрифт, Элемент.ЦветТекста, Элемент.ЦветФона, Элемент.Гиперссылка));
КонецЕсли;
КонецЦикла;
Возврат Новый ФорматированнаяСтрока(МассивРезультат);
КонецФункции
Важный нюанс: если искомая подстрока «разрезана» оформлением (например, часть слова жирная, а часть — обычная), этот метод потребует более сложной логики склейки текста перед поиском.
Иногда удобнее работать с текстом как с HTML-кодом. ФорматированныйДокумент позволяет получить HTML-содержимое, в котором оформление представлено стандартными тегами (<span>, <strong> и т.д.).
Посмотрим на последовательность действий:
Док = Новый ФорматированныйДокумент;
Док.УстановитьФорматированнуюСтроку(ТвояСтрока);
ТекстHTML = "";
Вложения = Новый Структура;
Док.ПолучитьHTML(ТекстHTML, Вложения);
// Теперь ТекстHTML содержит строку с тегами.
// ВНИМАНИЕ: Подстрока поиска может содержать спецсимволы HTML
ТекстHTML = СтрЗаменить(ТекстHTML, ПодстрокаПоиска, "");
Док.УстановитьHTML(ТекстHTML, Вложения);
Результат = Док.ПолучитьФорматированнуюСтроку();
Выясним причину осторожности при работе с HTML: если ваша подстрока содержит символы типа <, > или неразрывные пробелы, в HTML-коде они будут заменены на сущности типа <, > или . В этом случае обычный СтрЗаменить не найдет вхождение. Перед заменой рекомендуется привести искомую строку к HTML-безопасному формату.
Для продвинутых задач можно использовать механизм сериализации XDTO. ФорматированнаяСтрока отлично сериализуется в XML. В этом формате структура данных становится прозрачной: текст отделен от атрибутов оформления.
Проанализируем ситуацию программно:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьДанные(ЗаписьXML, МояФорматированнаяСтрока);
XMLТекст = ЗаписьXML.Закрыть();
// Выполняем замену в XML-структуре (аккуратно, чтобы не повредить теги)
XMLТекст = СтрЗаменить(XMLТекст, ПодстрокаПоиска, "");
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(XMLТекст);
Результат = СериализаторXDTO.ПрочитатьДанные(ЧтениеXML, Тип("ФорматированнаяСтрока"));
Этот метод является более стабильным, чем использование недокументированных функций типа ЗначениеВСтрокуВнутр(), так как опирается на стандартные механизмы платформы по работе с данными.
Если вы только проектируете систему, где требуется частое изменение частей форматированного текста, мы рекомендуем следующее:
ФорматированнаяСтрока, храните массив её частей. Если нужно «удалить» часть текста, просто удалите соответствующий элемент массива или замените его текст, после чего заново создайте итоговый объект через конструктор Новый ФорматированнаяСтрока(МассивЧастей).[ИмяКлиента]) и накладывайте оформление только в момент вывода пользователю.Выбор конкретного метода зависит от сложности вашего оформления и того, насколько точно нужно позиционировать замену. Для простых случаев достаточно перебора элементов, для сложных иерархических структур лучше использовать ФорматированныйДокумент.