КОНТЕКСТНО-ТАБЛИЧНОЕ РЕДАКТИРОВАНИЕ В ПРИМЕРАХ И ПОЯСНЕНИЯХ

 

 

Введение в контекстно-табличное редактирование

 

Контекстно-табличное редактирование определяется задаваемой таблицей контекстов, основными позициями которой являются “контекст поиска” и “контекст замены”. Для табличного набора таких простых контекстных замен действует основное правило редактирования:

 

Контекстная замена символов производится путем поиска всех контекстов таблицы последовательно, начиная с первого символа строки, затем второго, третьего и т.д. пока контекст не будет найден. После замены контекста поиск контекстов в строке продолжается с символа, стоящего во входной строке после найденного контекста, и начинается с начала таблицы. Поиск контекстов по строке прекращается при просмотре всех символов строки.

 

Однако следует иметь в виду, что сложные контекстные преобразования требуют явного управления процессом редактирования, а возможно и проведения аналитических действий:

 

-установка курсора в нужную позицию входной строки для начала поиска следующего контекста;
-установка номера входной строки таблицы контекстов и области чтения контекстов в таблице для текущего шага редактирования;
-установка смещения номера строки чтения входного файла (для возможности повторного считывания строки);
-возможность производить некоторые действия до начала и после окончания редактирования файла (например, устанавливать начальные параметры редактирования или производить коррекцию выходных результатов).

 

В общем случае, таблица контекстов имеет три области действия:

- начальный граничный контекст (контекст поиска имеет вид <!!> или <!>);
- контексты поиска/замены для строк файла;
- конечный граничный контекст (контекст поиска имеет вид <!!> или <!>).

 

Каждая из заданных областей таблицы подключается к работе только на своем этапе. Граничные контексты, начинающиеся с <!!> , работают только по одному разу в начале или в конце сеанса редактирования, а граничные контексты, начинающиеся с <!> , работают для каждого найденного в сеансе редактирования файла в начале или в конце его редактирования.

 

Возможность с помощью специальных средств осуществлять управление контекстным поиском и составляет сущность контекстно-табличного редактирования. Остановимся на некоторых пояснениях и примерах использования этих спецсредств.

 

В пакет спецсредств входит порядка десяти блоковых функций, которые позволяют в процессе контекстного редактирования выполнять дополнительные действия, в том числе и для найденных контекстов:

 

BlockOper

-

основные блоковые операции: сохранение, замена, вставка, удаление строк блока;

BlockConv

-

преобразование символов блока: кодировка, регистр;

BlockEdit

-

создание файлов с характеристиками состояния найденных контекстов в блоке редактирования;

BlockFile

-

работа с внешним файлом: выделение, запись контекстов;

BlockRepl

-

выполнение контекста замены через групповой оператор;

BlockLexm

-

работа с лексемами строки: чтение, добавление, замена, удаление;

BlockFind

-

работа с текстовой строкой: вхождение шаблона, длина строки и др.;

BlockEval

-

вычисление числовых подвыражений в строке;

BlockInfo

-

выдача информационной строки в StatusBar;

BlockList

-

работа со строками системного списка: загрузка, чтение, запись, добавление, замена, удаление, очистка и др.;

BlockForm

-

форматирование строки в рамках заданного поля шаблона;

BlockNumb

-

перевод целых числе между системами счисления ( от 2 до 36);

 

Например, функция BlockOper включает операции для установки курсора во входной строке (операция setst), установке указателя по таблице контекстов (операция setcn), задания смещения номера строки чтения входного файла (операция setln).

 

Одним из основных понятий контекстно-табличного редактирования является блок редактирования. Назначение блока редактирования многопланово. Чтобы понять его функцию рассмотрим применение блока редактирования на конкретных примерах.

 

Во-первых, блок редактирования определяет участок входного файла, задаваемый начальным и конечным контекстами поиска, образующими блоковый контекст. Блоковый контекст может иметь или не иметь номер |n|. Как правило, номера блоков существенны, если запрос содержит несколько блоков, содержащих внутри себя контексты поиска. В общем случае начало блока определяется конструкцией |n|%% <контекст поиска>, а конец блока конструкцией |n|$$<контекст поиска> (n либо пусто, либо целое положительное число). Фактически блок определяет создание внутреннего массива, возможно из нескольких найденных строк, который может быть каким-то образом отредактирован, а затем записан в выходной файл.

 

Пусть файл редактирования содержит строки:

…………………..

Первая покупка: N руб. N коп.

Вторая покупка: N руб. N коп.

Итого: N руб. N коп.

 

Каждое “N руб.” надо заменить соответственно на “550 руб.”, “700 руб.” и “1250 руб.”, а все “N коп.” - на “00 коп.”. Сделаем это, используя блоки редактирования:

 

Контекст поиска:

Контекст замены:

Комментарий:

|1|%%{Первая покупка:}

 

Начальный контекст блока.

|1|{N руб.}

“550 руб.”

Замена контекста внутри блока.

|1|$${N коп.}

“00 коп.”

Конечный контекст блока и его замена.

|2|%%{Вторая покупка:}

 

 

|2|{N руб.}

“700 руб.”

 

|2|$${N коп.}

“00 коп.”

 

|3|%%{Итого:}

 

 

|3|{N}{руб.}

#[“1250”//{1}]

Замена внутри блока контекста первой группы через групповое выражение.

|3|$${N коп.}

“00 коп.”

 

 

Каждый блок редактирования в примере состоит из отдельной строки файла, которая редактируется в процессе создания блока. Контексты поиска заданы в виде контекстных групп {…}, которых в строке поиска может находиться до 9 штук. Так в третьем блоке редактирования контекстная группа {N руб.} представлена в виде двух групп {N}{руб.} и тогда можно заменить только первую группу (мнемоника найденной контекстной группы {n}, где n может быть от 1 до 9), используя групповое выражение. Групповое выражение содержит, в общем виде, вычисляемое выражение в левой части и место замены на это выражение в правой части после символов “//”.

 

Во вторых, блок редактирования в таблице контекстов фактически является блоковым контекстом и определяет своим номером тот участок таблицы контекстов, по которому в данный момент надо производить дальнейший поиск и выполнение контекстной замены. В нашем примере контекст “N руб.” ищется только в том участке таблицы контекстов, c которого начинается найденный блок |n| (например, если найден контекст “ Вторая покупка:”, то при поиске контекста “N руб.”, выбирается контекст только из блока |2|{N руб.}). Конечно, для данного примера использование блоков необязательно, но в сложных контекстных преобразованиях без них просто не обойтись.

 

Редактирование может производиться до нахождения конца блока (как это сделано в предыдущем примере), а также после нахождения конца блока и определяться контекстами, стоящими за блоком редактирования и начинающимися с конструкции |n|%$ .Например, перевести строки редактирования в рассмотренном примере на верхний регистр.

 

Контекст поиска:

Контекст замены:

Комментарий:

|3|%%{Итого:}

 

Начальный контекст блока.

|3|{N руб.}

“1250 руб.”

 

|3|$${N коп.}

“00 коп.”

Конечный контекст блока.

|3|%$

#[BlockConv({10},,,,up,true)]

Перевод содержимого блока {10} на верхний регистр (параметр up) и запись в выходной файл (параметр true): Выходная строка: ИТОГО: 1250 РУБ. 00 КОП.

 

В-третьих, блоки редактирования позволяют организовать в таблице контекстов обращения с возвратом из одного блока редактирования (блокового контекста) в другой. Для этого, начальный контекст вызываемого блока должен иметь составной номер: <N основного блока>.<N вызывающего блока> , а вызывающий блок содержать обращение вида:

|n|%$                           #[BlockOper(reted,,,<N блока вызова>]

 

Пусть контекст замены для редактируемого файла хранится в первой ячейке общего архива в виде двух лексем, разделенных пробелом ( {{1}}: 550 700 ). Общий архив - это массив строк, каждая строка которого содержит некую текстовую информацию как в виде одной строки, так и в виде группы строк. В последнем случае строки представляются в ячейке архива разделенными символом EOLN (в его текстовом представлении). Область действия общего архива распространяется на весь сеанс редактирования, поэтому ячейки архива могут создаваться либо заранее (загружаться из файла), либо в процессе редактирования одного или нескольких файлов, а использоваться при редактировании любого файла. Мнемоника строк архива {{n}}, где n – целое положительное число от 1.

 

Контекст поиска:

Контекст замены:

Комментарий:

|1|%%{Первая покупка:}

 

Начальный контекст блока

|1|{N}

#[{{1}}|1''1//{1}]

Замена контекста внутри блока на первую лексему из первой ячейки общего архива ('' – два апострофа).

|1|$${коп.}

 

Конечный контекст блока.

|1|%$

#[BlockOper(reted,,,4)]

Уход в блок |4| и возврат на следующую ячейку текущего блока (она всегда должна быть); на вход вызываемому блоку всегда подается текущий блок редактирования и, возможно, следующая уже прочитанная информация из входного файла.

|1|%$

#[BlockOper(setst); BlockOper(setcn,,,1)]

Установка признака ненайденного контекста по операции setst (т.к. конечный контекст блока редактирования стоит в конце строки, следующая строка файла до ухода в другой блок была прочитана) и установка первой строки следующего поиска по таблице контекстов по операции setcn.

|2|%%{Вторая покупка:}

 

 

|2|{N}

#[{{1}}|2''2//{1}]

Замена контекста внутри блока на вторую лексему из первой ячейки общего архива.

|2|$${коп.}

 

 

|2|%$

#[BlockOper(reted,,,4)]

 

|2|%$

#[BlockOper(setst); BlockOper(setcn,,,1)]

 

|3|%%{Итого:}

 

 

|3|{N}

#[{{1}}|1''1 + {{1}}|2''2//{1}]

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

|3|$${коп.}

 

 

|3|%$

#[BlockOper(reted,,,4)]

 

|3|%$

#[BlockOper(setst); BlockOper(setcn,,,1)]

 

|4.1.2.3|%%{руб.}{[0-9]+}

#[“00”//{2}]

В процессе выполнения контекстных замен в блоках |1|, |2| и |3| были найдены и заменены контексты “N” не только для рублей, но и для копеек; начальный контекст блока |4| учитывает эту ситуацию путем задания во второй контекстной группе диапазона повторяющихся цифр поиска {[0-9]+}.

|4|$${коп.}

 

Конечный контекст блока и возврат в блок вызова.

 

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

|<номер блока>[<условное выражение> :: <условное выражение> :: …]|

Каждому условию в контексте замены соответствует своя последовательность контекстных преобразований, определяемая также по разделителю “::” . Основываясь на этом, первые три блока редактирования в нашем примере можно объединить в один:

 

Контекст поиска:

Контекст замены:

Комментарий:

|1|%%{Первая покупка:}

#[“1”//sysvar]

Начальный контекст блока; значение системной переменной sysvar полагается равным 1 .

|1|%%{Вторая покупка:}

#[“2”//sysvar]

Начальный контекст блока; значение системной переменной sysvar полагается равным 2 .

|1|%%{Итого:}

#[“0”//sysvar]

Начальный контекст блока; значение системной переменной sysvar полагается равным 0 .

|1[sysvar=“1” :: sysvar=“”2” :: sysvar=“0”]|{N}

#[{{1}}|1''1//{1} :: {{1}}|2''2//{1} :: {{1}}|1''1 + {{1}}|2''2//{1}]

Замена контекста внутри блока в зависимости от значения системной переменной sysvar .

|1|$${коп.}

 

Конечный контекст блока.

|1|%$

#[BlockOper(reted,,,4)]

Уход с возвратом в блок |4| .

|1|%$

#[BlockOper(setst); BlockOper(setcn,,,1)]

 

 

 

Пример 1

 

Входные файлы из разных каталогов содержат идентификатор Cum в строке определённого вида:  <META NAME="Description" CONTENT="Cum">

Требуется заменить контекст Cum на соответствующий пароль, находящийся в файле d:\exm\pass.txt  в виде отдельной строки. Причём для каждого следующего найденного входного файла пароль должен браться из следующей строки файла паролей. Выполнение этой задачи осуществляет файл запроса:

 

Контекст поиска:

Контекст замены:

Комментарий:

<!!>

#["0"//sysvar]

Обнуление системной переменной sysvar .

||%%{<META NAME="Description" CONTENT="Cum"}

 

 

Начальный контекст блока

$${>}

 

Конечный контекст блока.

%$

#[sysvar+1//sysvar; BlockFile(extr,d:\exm\pass.txt,{10}, "?*","","Cum","",true,,,, sysvar)]

 

Замена пароля

 

Здесь вводится системная переменная sysvar (все системные переменные записываются как sysvar ,sysvar1, sysvar2 и т.д.с указанием номера системной переменной). Граничный контекст <!!> выполняется один раз для текущего запроса редактирования и задает нулевое значение переменной sysvar. А далее для каждого файла редактирования эта системная переменная увеличивается на 1 и её значение подставляется в блоковую функцию BlockFile. Блоковая функция работает с блоком редактирования (его мнемоника {10}), он здесь задан без номера с начальным контекстом поиска  %%{…} и конечным контекстом поиска $${…}) после создания блока, на что указывает начальная конструкция %$ (все контексты замены, предваряемые символами %$ ,.всегда относятся к блоку редактирования после которого они непосредственно стоят в таблице контекстов). В параметрах блоковой функции задаются начальный контекст поиска по файлу "?*" (взять все символы строки от начала до конца) и начальный контекст блока редактирования "Cum" . Так как конечные контексты совпадают с начальными, то они задаются как "" . Параметр true означает, что после редактирования блок записывается в выходной файл.

 

 

Пример 2

 

Дан список идентификаторов: «a_a1__a21___ab___a0b385__b1a1_a11b___ab49____b1a003», подающийся в виде строки файла. Надо удалить из списка (разделитель один или более пробелов, которые здесь представлены символом подчеркивания) все идентификаторы, оканчивающиеся, по крайней мере, двумя цифрами, т.е. на выходе надо получить: «a_a1__ab___b1a1_a11b___». Выполнение этой задачи осуществляет файл запроса:

 

Контекст поиска:

Контекст замены:

Комментарий:

{[a-z][a-z0-9]* *}

#[BlockFind(postxt,{1},sysvar," "); BlockOper(setst)]

Поиск идентификатора и определение позиции начального пробела в найденном контексте.

|[loop({1}|sysvar-2..sysvar-1)/s=0^^9 :: else]|{[a-z][a-z0-9]* *}

#[""//{1}; BlockOper(setst,,,_{1}) :: ]

Повторный условный поиск идентификатора с использованием значения sysvar.

 

Идентификатор ищется с конечными пробелами (например, «a_»,«a1__»,«ab___») и в системную переменную sysvar заносится номер позиции первого пробела в контексте {1} или 0, если его там нет (функция BlockFind). Устанавливается поиск со следующей ячейки таблицы с той же позиции строки (функция BlockOper(setst)). При повторном нахождении этого идентификатора проверяется условие поиска, которое представляет собой цикл loop(…)/s по двум последним символам самого идентификатора, позиция которых определяется с учетом значения sysvar. Каждый такой символ последовательно сравнивается с цифрой 0,1,2,…9 диапазона 0^^9; и при удовлетворении условия для всех символов найденный контекст удаляется из списка, в противном случае найденный контекст сохраняется. Дальнейший поиск начнется с начала таблицы при выполнении условия с символа с номером _{1} -  начальный номер контекста в исходной строке, иначе с символа, следующего за контекстом {1}.

 

Усложним задачу, добавив еще одно условие удаления идентификатора из списка: следующий идентификатор списка не должен начинаться с буквы, отличной от начала найденного идентификатора, за исключением последнего элемента списка. На выходе надо получить список: «a_a1__a21___ab___b1a1_a11b__b1a003», При реализации этой задачи следует иметь в виду, что строка файла всегда заканчивается служебными символами \0d\0a (EOLN), поэтому за последним идентификатором списка стоит символ \0d. Выполнение этой задачи осуществляет файл запроса:

 

Контекст поиска:

Контекст замены:

Комментарий:

{[a-z][a-z0-9]* *[?\0d]}

#[BlockFind(postxt,{1},sysvar," "); BlockOper(setst)]

Поиск идентификатора и определение позиции начального пробела в найденном контексте.

|[sysvar=0 || loop({1}|-3..-2)/s=0^^9 || {1}|-1<>\0d || {1}|1<>{1}|-1 :: sysvar>0 || loop({1}|sysvar-2..sysvar-1)/s=0^^9 || {1}|-1<>\0d || {1}|1<>{1}|-1 :: else]|{[a-z][a-z0-9]* *[?\0d]}

 

#[{1}|-1..-1//{1}; BlockOper(setst,,,_{1}) :: >1 :: BlockOper(setst,,,{1}_)]

Повторный условный поиск идентификатора с использованием значения sysvar.

 

Здесь идентификатор ищется с последующими пробелами и произвольным начальным символом следующего идентификатора ( ? ) или с символом конца строки ( \0d ) (например, «a_a»,«a1__a»,«ab___b»,…«b1a003\0d») и при первом поиске также формируется значение системной переменной sysvar. Повторный поиск содержит более сложные условия: выборка последних двух символов идентификатора зависит от значения sysvar ( {1}|-3..-2  при sysvar=0, {1}|sysvar-2..sysvar-1 при sysvar>0); в условие также входит проверка не является ли последний символ концом строки ({1}|-1<>\0d ) и не совпадают ли начальные буквы соседних идентификаторов ({1}|1<>{1}|-1 ). При выполнении условия в контекст {1} заносится только последний символ, являющийся  началом следующего идентификатора ( {1}|-1..-1//{1} ) и дальнейший поиск начнется с начала таблицы с последнего символа контекста {1}, что обеспечивается операторами в зависимости от заданного условия: BlockOper(setst,,,_{1}) :: >1 :: BlockOper(setst,,,{1}_)]. Здесь _{1}- начальный номер контекста в исходной строке,  {1}_ - конечный номер контекста в исходной строке, >1 – операторы, соответствующие условию номер 1.