Моя первая публикация (чего-то полезного) не только на инфостарте, но вообще в интернете, так что особо сильно не пинайте
Тем, кому не интересна теория, предлагаю сразу перейти ко 2-му пункту, быстро прочесть как пользоваться и скопировать код методов из 3-го пункта. Некоторое время после написания публикации я буду находиться на поддержке, отвечать на вопросы (возможно исправлять баги).
Исправил редкий баг, всем кто пользовался до "10.06.2014 13:03:56" рекомендую обновить метод РаспарситьСтроку(Стр, ХешКодировки)
1. Теория и общее описание алгоритма.
Сразу хочу заметить, что не претендую на самое красивое, лучшее и оптимальное решение этой задачи, все ниже описанное можно и нужно оптимизировать и я даже знаю как, но только руки не доходят. Формат RTF очень старый и имеет много надстроек и дополнений, всю его спецификацию я разумеется не изучал. Если простыми словами, то основа формата блоки заключенные в фигурные скобки, вот такие "{ }". Внутри блока расположены команды-маркеры, которые начинаются с символа "\" и описывают шрифт, цвет фона текста и т.д. т.п. и текст (если блок текстовый). Таблицы в RTF легко склеиваются между собой достаточно просто в тексте удалить все символы, разделяющие таблицы. Честно говоря, иметь счастье разбиаться со спецификацией таблиц у меня не было ни какого желанию, по этому решено было использовть свойство склейки, просто повторяющуюся многократно строку необходимо заключить в секцию и многократно вывести. О секциях расскажу ниже. На этом рассмотрение теории по RTF можно закончить. Вы и сами можете по исследовать этот вопрос, создав файл *.rtf и открыв его хоть блокнотом, хоть Hex редактором. Алгоримтм на самом деле банален и прост. Читаем файл, превращаем в текст. Далее работаем с текстом. В первом приближении это всего два метода. Первый - это построить дерево из блоков, команд и текста. Второй - это превратить дерево в текст классическим рекурсивным обходом. Вот текст методов:
Функция РаспарситьСтрокуПерваяВерсия(Стр)
Перем Дерево, Стек, Мас, Анализ, Секции, Параметры;
Стек = Новый Массив;
Дерево = Новый Массив;
Мас = Дерево;
НачатоСлово = Ложь;
БылСлеш = ложь;
Темп = "";
Блок = Новый Структура("Дерево, Родитель");
ХешКодировки = ПолучитьХешКодировки();
Для х = 1 по СтрДлина(Стр) Цикл
Символ = Сред(Стр, х, 1);
Если НачатоСлово Тогда
Если Символ = " " Тогда
НачатоСлово = Ложь;
Темп = Темп + Символ;
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Темп = "";
Мас.Добавить(Анализ);
Продолжить;
КонецЕсли;
Если Символ = "{" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Мас.Добавить(Анализ);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "}" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Мас.Добавить(Анализ);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "\" Тогда
Если БылСлеш Тогда
БылСлеш = Ложь;
Иначе
БылСлеш = Истина;
КонецЕсли;
Иначе
БылСлеш = Ложь;
КонецЕсли;
Темп = Темп + Символ;
Иначе
Если Символ = "{" Тогда
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
Продолжить;
КонецЕсли;
Если Символ = "}" Тогда
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
Если Символ <> " " Тогда
Темп = Символ;
НачатоСлово = Истина;
Если Символ = "\" Тогда
БылСлеш = Истина;
Иначе
БылСлеш = Ложь;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат Дерево;
КонецФункции
и метод преобразования дерева в строку, совсем уж маленький
Функция СформироватьСтроку(Дерево) Экспорт
Перем рет, Элем;
рет = ""; Стек = Новый Массив;
Для каждого Элем из Дерево Цикл
Если Элем.Тип = "Блок" Тогда
рет = рет + "{" + СформироватьСтроку(Элем.Дерево) + "}";
Иначе
рет = рет + Элем.Текст;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Далее все усложняется тем, что необходимо помимо обычного текста отслеживать секции и параметры, запоминать их засосвывать в какую-нибудь структуру. В какую только ? Размумеется в такую, что бы было максимально просто и удобно работать как проффесионалу, так и новичку (ну что бы мозг не напрягался). В начале я думал построить такое же дерево, но это не удобано. в результате пришел к тому, что проще всего сделать Хеш (но 1С Соотвестсвие), в котром можно обратиться к нужному элементу (секции или параметру) по имени. Причем элемент Хеша это массив, на случай если в макете будут встречаться параметры или секции несколько раз. В результате, 1 раз заполнив параметр, он автоматический заполниться по всему документу, аналогично и относительно секции. Далее еще 2 метода, первый - это копирование секции, зная где ее начало и где конец, и второй - это заполнение параметра (просто подмена текста). Секции при копировании пропускают все управляющие блоки, т.е. теги секций и параметров. Все эти операции происходят прямо в дереве значений. Дерево по сути - это структура с большим количеством коротких строк, работать с которыми 1С-ке гораздо проще (я правда не знаю какой алгоритм для работы со строками использует фирма 1С, но в любом случае оперировать строками порядка 1-го мегобайта это не по феншую). В результате на пентиуме 4, шаблон размером под 1 мегобайт (почти 40 страниц) заполняется всего за 1 минуту. Для тех кто разберется со спецификацией RTF бонус в том, что уже все разложено по полочкам и можно менять шрифт, цвет и прочие параметры текста прямо на лету в самом шаблоне, хотя это можно делать и на уровне секций, например создать чередующуюся цветами таблицу.
2. Описание использования.
Описание использования состоит из 2-х частей: правила составления макетов (для пользователей) и правила вывода макетов (для программистов)
правила составления макетов (для пользователей)
- Секции оформляются 2-мя тегами: откытием секции и закрытием секции (Пример:...).
- Параметры оформляются одним тегом через квадратные скобки (Пример: [Параметр1]).
- Любой текст за пределами секции принадлежит документу и будет выводиться в любом случае (Пример: Этот текст выведется в любом случаеЭтот текст выведется только если будет выведена секция "ШапкаДокумента" значение параметра "тестовый" = [тестовый])
- Секции могут быть вложенными. (Пример:Шапка:Шапка:)
- Макет может состоять из произвольного количества секций и параметров. Правило составления секции аналогично правило расставлению скобок в обычно тексте. Нельзя что бы в любом месте количество закрывающих скобок было больше открывающих. На параметры нет никаких ограничений.
- Можно создавать параметры с одинаковым именем. При этом все параметры с одинаковым именем будут заполнены одним значением (Пример: Дата документа = [ДатаДокумента], и тут тоже быдет выведена такая же дата документа [ДатаДокумента]).
- Можно создавать секции с одинаковым именем, даже вложенные одна в другую. При этом они будут выведен столько раз, сколько задано в алгоритме. (Для примера предположим, что секция "Тестовая" выведена будет 2 раза и текст макета следующий: "1. Текст секции2. Текст вложенной". При таких условиях результатом будет текст: "1. Текст секции 2. Текст вложенной 2. Текст вложенной 1. Текст секции 2. Текст вложенной 2. Текст вложенной ")
- Правила составления имени секции или параметра. Можно использовать пробелы в имени (но не рекомендуется). Имена регистрозависимы ([ИмяПараметра] и [имяпараметра] это 2 разных параметра). Можно смело использовать цифры и спец символы, кроме "<", ">", "[", "]" или последовательности "
- Таблицы. Таблица формируется внутри секции. Телом секции является одна строка таблицы с параметрами. После объявления начала секции не должно быть ни текста ни пробелов, только перевод на новую строку. Аналогично для строки таблицы с параметрами и следующим за ней объявлением конца секции.
- Возможные ошибки. Не обязательно, но крайне рекомендуется обрамлять пробелами все теги секций и параметров (Пример: "Пробел""Пробел" текст секции "пробел"[Параметр]"пробел" конец текста секции "пробел""Пробел"
правила вывода макетов (для программистов)
-
Работа начинается с получения Хеша кодировки (Соответствия) функцией ПолучитьХешКодировки(). Все остальные функции используют этот Хеш.
-
Далее используется функция РаспарситьСтроку(Стр, ХешКодировки), Стр - это обычная строка (полученная считыванием файла-шаблона RTF). В результате возвтращается структура с 2-мя параметрами "Дерево" и "Секции". Дерево - это макет RTF в виде древовидной структуры. Секции - структура секций и параметров макета.
-
Далее вызывая методы ЗаполнитьПараметр(СтруктураСекций, ИмяПараметра, ЗначениеЗаполнения, ХешКодировки) и ВывестиСекцию(Запись, ИмяСекции, ЭтоТаблица = ложь, х_раз = 1) формируем результирующий документ (Это происходит очень быстро в памяти в структуре "Дерево"). В методе ВывестиСекцию параметр ЭтоТаблица уточняет обычная это секция или секция формирующая таблицу.
-
Правила вывода секций. Перед выводом секции необходимо заполнить параметры внутри ее тела (если это после, то вместо параметров будут пробелы). Сначала необходимо выводить вложенную секцию, после уровнем выше и т.д. до корневой.
-
После формирования документа нужно вызвать метод УдалитьСекции(Запись), Запись - это структура (с 2-мя параметрами "Дерево" и "Секции") возвтращаемая методом РаспарситьСтроку(Стр, ХешКодировки).
-
Последний шаг - функция СформироватьСтроку(Дерево). Возвращает результирующий документ в виде строки. Запиав строку в фаил RTF получим документ RTF.
Еще один момент. В зависимости от того в каком редакторе вы создавали RTF-фаил, внутренний текст может сильно различаться. Я писал код, тестировал и отлаживался на MS Word 2010-м. Правда я не думаю это на что-то может повлиять, но все же решил вас предупредить.
3. Код методов
Почему-то весь текст не влазит, разделил на 3 части.
Код методов из общего модуля:
Первая часть:
Процедура Печать(Элем, Данные) Экспорт
Перем Обработка, Спр, Двоичка, Темп;
Если ТипЗнч(Элем) = Тип("Строка") Тогда
Спр = Справочники.R_ПечатьМакетовRTF.НайтиПоНаименованию(Элем);
Если НЕ ЗначениеЗаполнено(Спр) Тогда
ВызватьИсключение "Элемент справочника " + Элем + " не найден";
КонецЕсли;
Иначе
Спр = Элем;
КонецЕсли;
Двоичка = Спр.Обработка.Получить();
Макет = Спр.Макет.Получить();
Если НЕ ЗначениеЗаполнено(Двоичка) Тогда
ВызватьИсключение "Обработка не выбрана";
КонецЕсли;
Темп = ПолучитьИмяВременногоФайла();
Двоичка.Записать(Темп);
Обработка = ВнешниеОбработки.Создать(Темп);
Обработка.Печать(Макет, Данные);
УдалитьФайлы(Темп);
КонецПроцедуры
Функция СформироватьСтроку(Дерево) Экспорт
Перем рет, Элем;
рет = ""; Стек = Новый Массив;
Для каждого Элем из Дерево Цикл
Если Элем.Тип = "Блок" Тогда
рет = рет + "{" + СформироватьСтроку(Элем.Дерево) + "}";
Иначе
рет = рет + Элем.Текст;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Функция РаспарситьСтроку(Стр, ХешКодировки) Экспорт
Перем Дерево, Стек, Мас, Анализ, Секции, НачатоСлово, БылСлеш, Параметр, Секция, Темп, Блок, СтруктураСекций;
Секции = Новый Массив;
Стек = Новый Массив;
Дерево = Новый Массив;
Мас = Дерево;
НачатоСлово = Ложь;
БылСлеш = Ложь;
Параметр = Неопределено;
Секция = Неопределено;
Темп = "";
Блок = Новый Структура("Дерево, Родитель");
Для х = 1 по СтрДлина(Стр) Цикл
Символ = Сред(Стр, х, 1);
Если НачатоСлово Тогда
Если Символ = " " Тогда
НачатоСлово = Ложь;
Темп = Темп + Символ;
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Темп = "";
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
Продолжить;
КонецЕсли;
Если Символ = "{" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "}" Тогда
Если БылСлеш Тогда
Темп = Темп + Символ;
Иначе
Анализ = АнализСлова(Блок, Темп, ХешКодировки);
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
Темп = "";
НачатоСлово = Ложь;
БылСлеш = Ложь;
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
КонецЕсли;
Продолжить;
КонецЕсли;
КонецЕсли;
Если Символ = "\" Тогда
Если БылСлеш Тогда
БылСлеш = Ложь;
Иначе
БылСлеш = Истина;
КонецЕсли;
Иначе
Если БылСлеш и Символ <> "u" и Символ <> "'" Тогда
Если Лев(Темп, 2) = "\'" или Лев(Темп, 2) = "\u" или Лев(Темп, 1) <> "\" Тогда
Анализ = АнализСлова(Блок, Лев(Темп, СтрДлина(Темп) - 1), ХешКодировки);
Темп = "\";
Если КомпоновкаКлючевыхСлов(Секции, Анализ, Секция, Параметр) Тогда
Мас.Добавить(Анализ);
КонецЕсли;
ПоискКлючевыхСлов(Секции, Анализ, Секция, Параметр);
КонецЕсли;
КонецЕсли;
БылСлеш = Ложь;
КонецЕсли;
Темп = Темп + Символ;
Иначе
Если Символ = "{" Тогда
Блок = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, ?(Стек.Количество() = 0, Неопределено, Стек[Стек.ВГраница()]));
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Добавить(Блок);
Мас.Добавить(Блок);
Мас = Блок.Дерево;
КонецЕсли;
Продолжить;
КонецЕсли;
Если Символ = "}" Тогда
Если КомпоновкаКлючевыхСлов(Секции, Блок, Секция, Параметр) Тогда
Стек.Удалить(Стек.ВГраница());
Если Стек.Количество() = 0 Тогда
Мас = Дерево;
Иначе
Мас = Стек[Стек.ВГраница()].Дерево;
КонецЕсли;
КонецЕсли;
Продолжить;
КонецЕсли;
Темп = Символ;
НачатоСлово = Истина;
Если Символ = "\" Тогда
БылСлеш = Истина;
Иначе
БылСлеш = Ложь;
КонецЕсли;
КонецЕсли;
КонецЦикла;
СтруктураСекций = СформироватьСтруктуруСекций(Дерево, Секции);
Возврат Новый Структура("Дерево, Секции", Дерево, СтруктураСекций);
КонецФункции
Функция ПолучитьХешКодировки() Экспорт
Перем Хеш;
Хеш = Новый Соответствие;
Хеш["\'c0"] = "А";
Хеш["\'c1"] = "Б";
Хеш["\'c2"] = "В";
Хеш["\'c3"] = "Г";
Хеш["\'c4"] = "Д";
Хеш["\'c5"] = "Е";
Хеш["\'c6"] = "Ж";
Хеш["\'c7"] = "З";
Хеш["\'c8"] = "И";
Хеш["\'c9"] = "Й";
Хеш["\'ca"] = "К";
Хеш["\'cb"] = "Л";
Хеш["\'cc"] = "М";
Хеш["\'cd"] = "Н";
Хеш["\'ce"] = "О";
Хеш["\'cf"] = "П";
Хеш["\'d0"] = "Р";
Хеш["\'d1"] = "С";
Хеш["\'d2"] = "Т";
Хеш["\'d3"] = "У";
Хеш["\'d4"] = "Ф";
Хеш["\'d5"] = "Х";
Хеш["\'d6"] = "Ц";
Хеш["\'d7"] = "Ч";
Хеш["\'d8"] = "Ш";
Хеш["\'d9"] = "Щ";
Хеш["\'da"] = "Ъ";
Хеш["\'db"] = "Ы";
Хеш["\'dc"] = "Ь";
Хеш["\'dd"] = "Э";
Хеш["\'de"] = "Ю";
Хеш["\'df"] = "Я";
Хеш["\'e0"] = "а";
Хеш["\'e1"] = "б";
Хеш["\'e2"] = "в";
Хеш["\'e3"] = "г";
Хеш["\'e4"] = "д";
Хеш["\'e5"] = "е";
Хеш["\'e6"] = "ж";
Хеш["\'e7"] = "з";
Хеш["\'e8"] = "и";
Хеш["\'e9"] = "й";
Хеш["\'ea"] = "к";
Хеш["\'eb"] = "л";
Хеш["\'ec"] = "м";
Хеш["\'ed"] = "н";
Хеш["\'ee"] = "о";
Хеш["\'ef"] = "п";
Хеш["\'f0"] = "р";
Хеш["\'f1"] = "с";
Хеш["\'f2"] = "т";
Хеш["\'f3"] = "у";
Хеш["\'f4"] = "ф";
Хеш["\'f5"] = "х";
Хеш["\'f6"] = "ц";
Хеш["\'f7"] = "ч";
Хеш["\'f8"] = "ш";
Хеш["\'f9"] = "щ";
Хеш["\'fa"] = "ъ";
Хеш["\'fb"] = "ы";
Хеш["\'fc"] = "ь";
Хеш["\'fd"] = "э";
Хеш["\'fe"] = "ю";
Хеш["\'ff"] = "я";
Хеш["\u1040"] = "А";
Хеш["\u1041"] = "Б";
Хеш["\u1042"] = "В";
Хеш["\u1043"] = "Г";
Хеш["\u1044"] = "Д";
Хеш["\u1045"] = "Е";
Хеш["\u1046"] = "Ж";
Хеш["\u1047"] = "З";
Хеш["\u1048"] = "И";
Хеш["\u1049"] = "Й";
Хеш["\u1050"] = "К";
Хеш["\u1051"] = "Л";
Хеш["\u1052"] = "М";
Хеш["\u1053"] = "Н";
Хеш["\u1054"] = "О";
Хеш["\u1055"] = "П";
Хеш["\u1056"] = "Р";
Хеш["\u1057"] = "С";
Хеш["\u1058"] = "Т";
Хеш["\u1059"] = "У";
Хеш["\u1060"] = "Ф";
Хеш["\u1061"] = "Х";
Хеш["\u1062"] = "Ц";
Хеш["\u1063"] = "Ч";
Хеш["\u1064"] = "Ш";
Хеш["\u1065"] = "Щ";
Хеш["\u1066"] = "Ъ";
Хеш["\u1067"] = "Ы";
Хеш["\u1068"] = "Ь";
Хеш["\u1069"] = "Э";
Хеш["\u1070"] = "Ю";
Хеш["\u1071"] = "Я";
Хеш["\u1072"] = "а";
Хеш["\u1073"] = "б";
Хеш["\u1074"] = "в";
Хеш["\u1075"] = "г";
Хеш["\u1076"] = "д";
Хеш["\u1077"] = "е";
Хеш["\u1078"] = "ж";
Хеш["\u1079"] = "з";
Хеш["\u1080"] = "и";
Хеш["\u1081"] = "й";
Хеш["\u1082"] = "к";
Хеш["\u1083"] = "л";
Хеш["\u1084"] = "м";
Хеш["\u1085"] = "н";
Хеш["\u1086"] = "о";
Хеш["\u1087"] = "п";
Хеш["\u1088"] = "р";
Хеш["\u1089"] = "с";
Хеш["\u1090"] = "т";
Хеш["\u1091"] = "у";
Хеш["\u1092"] = "ф";
Хеш["\u1093"] = "х";
Хеш["\u1094"] = "ц";
Хеш["\u1095"] = "ч";
Хеш["\u1096"] = "ш";
Хеш["\u1097"] = "щ";
Хеш["\u1098"] = "ъ";
Хеш["\u1099"] = "ы";
Хеш["\u1100"] = "ь";
Хеш["\u1101"] = "э";
Хеш["\u1102"] = "ю";
Хеш["\u1103"] = "я";
Хеш["А"] = "\'c0";
Хеш["Б"] = "\'c1";
Хеш["В"] = "\'c2";
Хеш["Г"] = "\'c3";
Хеш["Д"] = "\'c4";
Хеш["Е"] = "\'c5";
Хеш["Ж"] = "\'c6";
Хеш["З"] = "\'c7";
Хеш["И"] = "\'c8";
Хеш["Й"] = "\'c9";
Хеш["К"] = "\'ca";
Хеш["Л"] = "\'cb";
Хеш["М"] = "\'cc";
Хеш["Н"] = "\'cd";
Хеш["О"] = "\'ce";
Хеш["П"] = "\'cf";
Хеш["Р"] = "\'d0";
Хеш["С"] = "\'d1";
Хеш["Т"] = "\'d2";
Хеш["У"] = "\'d3";
Хеш["Ф"] = "\'d4";
Хеш["Х"] = "\'d5";
Хеш["Ц"] = "\'d6";
Хеш["Ч"] = "\'d7";
Хеш["Ш"] = "\'d8";
Хеш["Щ"] = "\'d9";
Хеш["Ъ"] = "\'da";
Хеш["Ы"] = "\'db";
Хеш["Ь"] = "\'dc";
Хеш["Э"] = "\'dd";
Хеш["Ю"] = "\'de";
Хеш["Я"] = "\'df";
Хеш["а"] = "\'e0";
Хеш["б"] = "\'e1";
Хеш["в"] = "\'e2";
Хеш["г"] = "\'e3";
Хеш["д"] = "\'e4";
Хеш["е"] = "\'e5";
Хеш["ж"] = "\'e6";
Хеш["з"] = "\'e7";
Хеш["и"] = "\'e8";
Хеш["й"] = "\'e9";
Хеш["к"] = "\'ea";
Хеш["л"] = "\'eb";
Хеш["м"] = "\'ec";
Хеш["н"] = "\'ed";
Хеш["о"] = "\'ee";
Хеш["п"] = "\'ef";
Хеш["р"] = "\'f0";
Хеш["с"] = "\'f1";
Хеш["т"] = "\'f2";
Хеш["у"] = "\'f3";
Хеш["ф"] = "\'f4";
Хеш["х"] = "\'f5";
Хеш["ц"] = "\'f6";
Хеш["ч"] = "\'f7";
Хеш["ш"] = "\'f8";
Хеш["щ"] = "\'f9";
Хеш["ъ"] = "\'fa";
Хеш["ы"] = "\'fb";
Хеш["ь"] = "\'fc";
Хеш["э"] = "\'fd";
Хеш["ю"] = "\'fe";
Хеш["я"] = "\'ff";
Возврат Хеш;
КонецФункции
Процедура ЗаполнитьПараметр(СтруктураСекций, ИмяПараметра, ЗначениеЗаполнения, ХешКодировки) Экспорт
Перем х, Темп, Символ, Элем, Параметр, Значение;
Параметр = СтруктураСекций.Параметры[ИмяПараметра];
Если НЕ ЗначениеЗаполнено(Параметр) Тогда
ВызватьИсключение "Не существует параметра с именем " + ИмяПараметра;
КонецЕсли;
Значение = Строка(ЗначениеЗаполнения);
Темп = "";
Для х = 1 по СтрДлина(Значение) Цикл
Символ = Сред(Значение, х, 1);
Если Символ = "\" Тогда
Темп = Темп + "\\";
Продолжить;
КонецЕсли;
Если Символ = "{" Тогда
Темп = Темп + "\{";
Продолжить;
КонецЕсли;
Если Символ = "}" Тогда
Темп = Темп + "\}";
Продолжить;
КонецЕсли;
ХешСимвол = ХешКодировки[Символ];
Темп = ?(ЗначениеЗаполнено(ХешСимвол), Темп + ХешСимвол, Темп + Символ);
КонецЦикла;
Для каждого Элем из Параметр Цикл
Элем.Текст = Темп;
КонецЦикла;
КонецПроцедуры
Вторая часть:
Процедура ВывестиСекцию(Запись, ИмяСекции, ЭтоТаблица = ложь, х_раз = 1) Экспорт
//Можно конечно в цикле вызывать метод ВывестиСекцию(Дерево, МасСекций)
//Но лучше использовать параметр "х_раз", это ускорит работу
Перем у, х, Мас, ИндексНачала, ИндексКонца, Элем, Секция, ТемпМас, раз, ХешСекций, МасСекций;
Перем Дерево, СтруктураСекций, НачалоСекции;
Дерево = Запись.Дерево;
СтруктураСекций = Запись.Секции;
ХешСекций = СтруктураСекций.ХешСекций;
МасСекций = СтруктураСекций.Секции[ИмяСекции];
Если НЕ ЗначениеЗаполнено(МасСекций) Тогда
ВызватьИсключение "Не найдена секция с именем " + ИмяСекции;
КонецЕсли;
у = МасСекций.Вграница();
Пока у >= 0 Цикл
Секция = МасСекций[у];
ИндексНачала = -1;
ИндексКонца = -1;
Мас = ?(ЗначениеЗаполнено(Секция.БлокНачала.Родитель), Секция.БлокНачала.Родитель.Дерево, Дерево);
Для х = 0 по Мас.Вграница() Цикл
Если Мас[х] = Секция.БлокНачала Тогда
ИндексНачала = х;
Для х = ИндексНачала по Мас.Вграница() Цикл
Если Мас[х] = Секция.БлокКонца Тогда
ИндексКонца = х;
Прервать;
КонецЕсли;
КонецЦикла;
Прервать;
КонецЕсли;
КонецЦикла;
ТемпМас = Новый Массив;
НачалоСекции = Ложь;
Для х = ИндексНачала + 1 + ?(ЭтоТаблица, 1, 0) по ИндексКонца - 1 Цикл
Если НачалоСекции Тогда
Если ХешСекций[Мас[х]] = "Конец" Тогда
НачалоСекции = Ложь;
Продолжить;
КонецЕсли;
Иначе
Если ХешСекций[Мас[х]] = "Начало" Тогда
НачалоСекции = Истина;
Продолжить;
Иначе
ТемпМас.Добавить(Мас[х]);
КонецЕсли;
КонецЕсли;
КонецЦикла;
Для раз = 1 по х_раз Цикл
х = ТемпМас.ВГраница();
Пока х >= 0 Цикл
Элем = СкопироватьЭлементДерева(ТемпМас[х], ТемпМас[х].Родитель);
Мас.Вставить(ИндексНачала, Элем);
х = х - 1;
КонецЦикла;
КонецЦикла;
у = у - 1;
КонецЦикла;
КонецПроцедуры
Процедура УдалитьСекции(Запись) Экспорт
Перем Мас, Секция, НачатоУдаление, х, Элем, МасСекций, Дерево, СтруктураСекций;
Дерево = Запись.Дерево;
СтруктураСекций = Запись.Секции;
Для Каждого Элем из СтруктураСекций.Секции Цикл
МасСекций = Элем.Значение;
у = МасСекций.Вграница();
Пока у >= 0 Цикл
Секция = МасСекций[у];
Мас = ?(ЗначениеЗаполнено(Секция.БлокНачала.Родитель), Секция.БлокНачала.Родитель.Дерево, Дерево);
х = 0;
НачатоУдаление = Ложь;
Пока х 0 Тогда
Если Найти(Элем.Текст, ">") > 0 Тогда
ДобавлениеСекции(Секции, Элем, "Секция");
Иначе
Секция = Элем;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(Секция) Тогда
Если (Элем.Тип = "Текст") Тогда
Если Найти(Элем.Текст, "[") > 0 Тогда
Если Найти(Элем.Текст, "]") > 0 Тогда
ДобавлениеСекции(Секции, Элем, "Параметр");
Иначе
Параметр = Элем;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция КомпоновкаКлючевыхСлов(Секции, Элем, Секция, Параметр)
Перем рет;
рет = Истина;
Если ЗначениеЗаполнено(Секция) Тогда
рет = Ложь;
Если Элем.Тип = "Текст" Тогда
Секция.Текст = Секция.Текст + Элем.Текст;
Секция.Значение = Секция.Значение + Элем.Значение;
Если Найти(Элем.Текст, ">") > 0 Тогда
ДобавлениеСекции(Секции, Секция, "Секция");
Секция = Неопределено;
КонецЕсли;
КонецЕсли;
Иначе
Если ЗначениеЗаполнено(Параметр) Тогда
рет = Ложь;
Если Элем.Тип = "Текст" Тогда
Параметр.Текст = Параметр.Текст + Элем.Текст;
Параметр.Значение = Параметр.Значение + Элем.Значение;
Если Найти(Элем.Текст, "]") > 0 Тогда
ДобавлениеСекции(Секции, Параметр, "Параметр");
Параметр = Неопределено;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат рет;
КонецФункции
Процедура ДобавлениеСекции(Секции, Секция, Тип = "Параметр")
Секции.Добавить(Новый Структура("Секция, Тип", Секция, Тип));
КонецПроцедуры
Функция АнализСлова(Блок, Стр, ХешКодировки)
Перем рет, х, Символ, БылСлеш, у, Значение, Темп, ЗначениеКодировки;
ЭтоТекст = Ложь;
Символ = Сред(Стр, 1, 1);
Если КодСимвола(Символ, 1) = 13 Тогда
Символ = Сред(Стр, 2, 1);
Если Символ = "\" Тогда
Символ = Сред(Стр, 3, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
Если КодСимвола(Символ, 1) = 10 Тогда
Символ = Сред(Стр, 3, 1);
Если Символ = "\" Тогда
Символ = Сред(Стр, 4, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
ЭтоТекст = Истина;
КонецЕсли;
Иначе
Если Символ = "\" Тогда
Символ = Сред(Стр, 2, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
ЭтоТекст = Истина;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Иначе
Если Символ = "\" Тогда
Символ = Сред(Стр, 2, 1);
Если Символ = "'" или Символ = "u" Тогда
ЭтоТекст = Истина;
КонецЕсли;
Иначе
ЭтоТекст = Истина;
КонецЕсли;
КонецЕсли;
Если ЭтоТекст Тогда
у = 0; Темп = ""; БылСлеш = Ложь; Значение = "";
Для х = 1 по СтрДлина(Стр) Цикл
Символ = Сред(Стр, х, 1);
Если БылСлеш Тогда
Если Символ = "\" или Символ = "{" или Символ = "}" Тогда
Значение = Значение + Символ;
БылСлеш = Ложь;
Продолжить;
КонецЕсли;
Если Символ = "'" Тогда
Темп = "\'";
у = 2;
БылСлеш = Ложь;
Продолжить;
КонецЕсли;
Если Символ = "u" Тогда
Темп = "\u";
у = 4;
БылСлеш = Ложь;
Продолжить;
КонецЕсли;
Иначе
Если у > 0 Тогда
у = у - 1;
Темп = Темп + Символ;
Если у = 0 Тогда
ЗначениеКодировки = ХешКодировки[Темп];
Если ЗначениеЗаполнено(ЗначениеКодировки) Тогда
Значение = Значение + ЗначениеКодировки;
КонецЕсли;
КонецЕсли;
Продолжить;
КонецЕсли;
Если Символ = "\" Тогда
БылСлеш = Истина;
Продолжить;
КонецЕсли;
Значение = Значение + Символ;
КонецЕсли;
КонецЦикла;
рет = Новый Структура("Тип, Текст, Значение, Родитель", "Текст", Стр, Значение, Блок);
Иначе
рет = Новый Структура("Тип, Текст, Родитель", "Команда", Стр, Блок);
КонецЕсли;
Возврат рет;
КонецФункции
Третья часть:
Функция СформироватьСтруктуруСекций(Дерево, Секции)
Перем рет, Темп, Запись, Мас, ТемпМас;
рет = новый Структура("Секции, Параметры, ХешСекций");
рет.Секции = Новый Соответствие;
рет.Параметры = Новый Соответствие;
рет.ХешСекций = Новый Соответствие;
Для каждого Элем из Секции Цикл
Если Элем.Тип = "Параметр" Тогда
Темп = Элем.Секция.Значение;
Темп = ОчиститьНазвание(Темп);
Если рет.Параметры[Темп] = Неопределено Тогда
ТемпМас = Новый Массив;
ТемпМас.Добавить(Элем.Секция);
рет.Параметры[Темп] = ТемпМас;
Иначе
рет.Параметры[Темп].Добавить(Элем.Секция);
КонецЕсли;
Элем.Секция.Текст = "";
Продолжить;
КонецЕсли;
Если Элем.Тип = "Секция" Тогда
Запись = ОчиститьСекцию(Дерево, Элем);
Если рет.Секции[Запись.Название] = Неопределено Тогда
Если Запись.ТипСекции = "Конец" Тогда
ВызватьИсключение "Конец секции не может располагаться до ее начала";
КонецЕсли;
Темп = Новый Структура("Дерево, БлокНачала, БлокКонца", Запись.Дерево, Запись.БлокСекции, Неопределено);
ТемпМас = Новый Массив;
ТемпМас.Добавить(Темп);
рет.Секции[Запись.Название] = ТемпМас;
Иначе
Если Запись.ТипСекции = "Начало" Тогда
Темп = Новый Структура("Дерево, БлокНачала, БлокКонца", Запись.Дерево, Запись.БлокСекции, Неопределено);
рет.Секции[Запись.Название].Добавить(Темп);
Иначе
Темп = рет.Секции[Запись.Название];
х = Темп.Вграница();
Пока х >= 0 Цикл
Если Темп[х].БлокКонца = Неопределено Тогда
ПоискКонцаСекции(Дерево, Запись, Темп[х]);
Прервать;
КонецЕсли;
х = х - 1;
КонецЦикла;
КонецЕсли;
КонецЕсли;
рет.ХешСекций[Запись.БлокСекции] = Запись.ТипСекции;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Процедура ПоискКонцаСекции(Дерево, ЗаготовкаСекции, ЭлементСекции)
Перем Блок, Элем, ПоследнийБлок;
Блок = ЗаготовкаСекции.БлокСекции;
Пока ЗначениеЗаполнено(Блок) Цикл
Если ЗаготовкаСекции.Дерево = ЭлементСекции.Дерево Тогда
ЭлементСекции.БлокКонца = ЗаготовкаСекции.БлокСекции;
Прервать;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(Блок.Родитель) Тогда
БлокСекции = Блок;
КонецЕсли;
Блок = Блок.Родитель;
КонецЦикла;
Если НЕ ЗначениеЗаполнено(ЭлементСекции.БлокКонца) Тогда
Если ЗаготовкаСекции.Дерево = Дерево Тогда
Для каждого Элем из Дерево Цикл
Если Элем = БлокСекции Тогда
ЭлементСекции.БлокКонца = Элем;
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(ЭлементСекции.БлокКонца) Тогда
ЭлементСекции.БлокКонца = ЭлементСекции.БлокНачала;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Функция ОчиститьНазвание(ОчищаемоеНазвание)
Перем рет, х, Символ, Название;
рет = ""; Название = СокрЛП(ОчищаемоеНазвание);
Для х = 1 по СтрДлина(Название) Цикл
Символ = Сред(Название, х, 1);
Если Символ = "]" Тогда
Прервать;
Иначе
Если НЕ (КодСимвола(Символ, 1) = 13 или КодСимвола(Символ, 1) = 10 или Символ = "[") Тогда
рет = рет + Символ;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Возврат рет;
КонецФункции
Функция СкопироватьЭлементДерева(Элем, Родитель)
Перем х, рет, НовЭлем;
Если Элем.Тип = "Блок" Тогда
рет = Новый Структура("Тип, Дерево, Родитель", "Блок", Новый Массив, Родитель);
Для х = 0 по Элем.Дерево.ВГраница() Цикл
НовЭлем = СкопироватьЭлементДерева(Элем.Дерево[х], рет);
рет.Дерево.Добавить(НовЭлем);
КонецЦикла;
ИначеЕсли Элем.Тип = "Текст" Тогда
рет = Новый Структура("Тип, Текст, Значение, Родитель", "Текст", Элем.Текст, Элем.Значение, Элем.Родитель);
ИначеЕсли Элем.Тип = "Команда" Тогда
рет = Новый Структура("Тип, Текст, Родитель", "Команда", Элем.Текст, Элем.Родитель);
Иначе
ВызватьИсключение "Не ожиданный элемент тип = " + Элем.Тип;
КонецЕсли;
Возврат рет;
КонецФункции
Функция ОчиститьСекцию(Дерево, Секция)
Перем рет, Темп, х;
рет = Новый Структура("Дерево, БлокСекции, Название, ТипСекции");
Темп = ОчиститьНазвание(СокрЛП(Секция.Секция.Значение));
Если Лев(Темп, 2) = "</" Тогда
рет.ТипСекции = "Конец";
рет.Название = Сред(Темп, 3, СтрДлина(Темп) - 3);
ИначеЕсли Лев(Темп, 1) = "<" Тогда
рет.ТипСекции = "Начало";
рет.Название = Сред(Темп, 2, СтрДлина(Темп) - 2);
Иначе
ВызватьИсключение "Невозможно определить тип секции";
КонецЕсли;
рет.Название = ОчиститьНазвание(рет.Название);
Секция.Секция.Текст = "";
Темп = Секция.Секция.Родитель.Родитель;
рет.БлокСекции = Секция.Секция.Родитель;
рет.Дерево = ?(ЗначениеЗаполнено(Темп), Темп.Дерево, Дерево);
Возврат рет;
КонецФункции