Регулярные выражения в WinEdt: поиск формул с неиспользуемыми номерами

в 11:54, , рубрики: latex, regular expressions, Регулярные выражения, метки: ,

После более детального ознакомления с мануалом редактора WinEdt (предназначенного почти исключительно для создания LaTeX-документов) открыл дополнительные возможности инструмента поиска/замены этой программы. Чтобы активировать «умный» поиск, нужно поставить галочку в чекбоксе Regular Expressions в меню Find или Find and Replace, в результате чего строка поиска превратится, по сути, в командную строку, с помощью которой можно творить чудеса. То есть сделать с текстом можно будет практически всё, другой вопрос, что иногда чересчур извращённо (поэтому в случае серьёзных задач создание соответствующих макросов выглядит более уместным).

Анекдот про гинеколога

Гинеколог приходит устраиваться на работу в автосервис. Его просят разобрать-собрать двигатель. Он выполняет и интересуется оценкой своей работы. Ему отвечают: «в принципе ничего, только вот мы первый раз видим, чтобы всё это делалось через выхлопную трубу».

Приведу пример. Нужно найти все неиспользуемые метки label, то есть те, на которые в тексте работы нет ни одной ref-ссылки (all labels that are never referred to, как сказано в англоязычном руководстве). Лишняя метка, безобидная сама по себе, может сигнализировать, в частности, об избыточной «занумерованности» формул латеховского документа (т. е. о наличии в нём формул с неиспользуемыми номерами). В случае, если текст достаточно большой и имеет много занумерованных соотношений, возникновение таких меток практически неизбежно (вы когда-то ссылались на данное уравнение, потом изменили текст, удалив ссылку, а убрать номер у уравнения, скорее всего, забыли). Вместе с тем, ручное обнаружение «лишних» меток превращается (опять-таки в силу большого объёма материала) в чересчур громоздкую и, главное тупую механическую работу, характер которой просто вопиёт о рациональной альтернативе.

Итак, решим поставленную задачу с помощью «умного» поиска редактора WinEdt (версии 5.3 заведомо должно хватить). Прежде всего, замечу, что WinEdt резервирует ячейки памяти (регистры) с именами %!0, ..., %!9 для пользовательских нужд. Причём надо иметь в виду, что память эта существенно оперативна в том смысле, что она обнуляется при каждом перезапуске WinEdt. Используем эту память, чтобы сохранить содержимое всех ссылок ref в виде одной длинной строки: нажимаем ctrl+F, не забываем про галочку в чекбоксе Regular Expressions открывшегося меню, в строке поиска которого вводим следующий текст:

\ref({*})X{"|GetTag(0,0);LetReg(1,"%!1%!0");|}

Регулярные выражения в WinEdt: поиск формул с неиспользуемыми номерами

Некоторые пояснения (частично раскрывающие смысл последней строки). При включённом режиме Regular Expressions некоторые символы (например , { и }) приобретают служебный смысл; если же они нужны нам в своём непосредственном значении (то есть как соответствующие символы), их следует использовать вместе с косой чертой впереди (например \, { и }). Но есть и исключения: например, круглые скобки сами по себе не являются служебными символами (тем самым, означая буквально круглые скобки), а вот в сочетаниях ( и ), наоборот, имеют специальный смысл. Текст, заключённый между ( и ), превращается в так называемый тег (выражение-метку или отмеченный текст) и может быть использован в дальнейшем: обращение к этому тексту (например, в той же строке поиска для обнаружения повторяющегося фрагмента или в строке «заменить на») осуществляется посредством команды (нуль — это устанавливаемый по умолчанию номер затегированного фрагмента). Если есть необходимость в выделении нескольких частей, следует использовать конструкции вида:

(0 какой-то текст ), …, (9 какой-то текст )

и команды , …, 9 для обращения к соответствующим частям.

А что там за звёздочка * между { и } в начале введённого текста? Эта звёздочка называется шаблоном и означает произвольную последовательность символов (в том числе пустую) в пределах одной строки (замечу, что, начиная с версии WinEdt 5.3, сочетание ** кодирует произвольный текст, включающий разрывы строк).

Таким образом, набор символов:

\ref({*})

то есть первая часть рассматриваемого выражения, задаёт поиск любых сочетаний вида: ref{произвольный текст}. При обнаружении такого сочетания, запускаются макросы, о чём свидетельствует вторая часть выражения, начинающаяся с X (при отсутствии команды запуска макросов, WinEdt просто переходит к найденному сочетанию, выделяя его в тексте документа). Причём команда запуска макросов может начинаться и с x (регистр имеет значение!), а также с Xx и xX. Дело в том, что в зависимости от результатов исполнения макросов WinEdt может как перейти к найденному фрагменту (в нашем случае это — ref{произвольный текст}), так и проигнорировать его (как если бы он отличался от заданного в поисковой строке), перейдя к поиску следующего совпадения. А то, какую из этих двух альтернатив он предпочтёт, определяется регистром “x-команды” и значением используемой WinEdt булевой переменной IFOK (по умолчанию равном истине), которое часть макросов могут изменять. В случае команды X реакция WinEdt согласуется со значением IFOK: если значение IFOK равно истине, WinEdt переходит к найденному фрагменту; если же оно равно лжи, WinEdt игнорирует этот фрагмент. В случае команды x реакция WinEdt на значение IFOK прямо противоположна, а при использовании Xx или xX, WinEdt независимо от значения IFOK отображает обнаруженный текст.

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

X{"|GetTag(0,0);LetReg(1,"%!1%!0");|}

Она запускает два макроса: GetTag(0,0) и LetReg(1,"%!1%!0"). Макрос GetTag(n,m) записывает содержимое n-го тега (в нашем случае нулевого тега, т. е. аргумента команды ref вместе с обрамляющими его фигурными скобками) в m-й регистр, т. е. в ячейку памяти с именем %!m (в нашем случае – с именем %!0). Макрос LetReg(k,«строка») записывает в k-ю ячейку свой второй аргумент (без обрамляющих кавычек). Получается, что в нашем случае LetReg перезаписывает первый регистр (изначально в нём ничего нет), добавляя к нему содержимое 0-го регистра, т. е. заключённый в фигурные скобки аргумент обнаруженной поиском WinEdt команды ref. Тем самым, для занесения в ячейку %!0 последовательности аргументов всех команд ref, встречающихся в тексте, можно, введя в поисковую строку:

\ref({*})X{"|GetTag(0,0);LetReg(1,"%!1%!0");|}

пройтись поиском через весь документ. Это делается сравнительно просто и быстро: после первого успешного обнаружения заданного текста поиск всех последующих вхождений осуществляем нажимая и удерживая клавишу F3 (для документа, содержащего многие сотни занумерованных соотношений, удерживать F3 пришлось не более 30 секунд). Впрочем, есть и альтернативный вариант — можно воспользоваться инструментом замены редактора WinEdt: нажимаем ctrl+R, в строке поиска вводим:

\ref({*})X{"|GetTag(0,0);LetReg(1,"%!1%!0");|}

в строке «заменить на»:

ref({*})

при появлении запроса о подтверждении замены выбираем All и вперёд (не забываем про галочку в чекбоксе Regular Expressions).

Подготовительная работа завершена. Теперь обнаружение неиспользуемых меток осуществляется вызовом поиска с выражением:

\label{{(*)}}x{FindInString("%!1","");}

в поисковой строке (вспомните о связи x c IFOK!). Поиск с аргументом:

\label{{(*)}}X{FindInString("%!1","");}

решает обратный вопрос, показывая лишь те метки, которые фигурируют в аргументе хотя бы одной из ref-команд. Замечу, что наличие внешних фигурных скобок в выражении: \label{({*})} является синтаксически излишним, однако в случае их отсутствия поиск WinEdt даёт, вообще говоря, некорректный результат. Эта особенность не имеет рационального объяснения — её необходимо запомнить (в англоязычном руководстве просто сказано, что it is important to use: {{(*)}} because {(*)} will not work here!).

Анекдот про грузинскую школу

Учитель на уроке русского языка в грузинской школе: «Дети, запомните: слова сол, фасол и вермишел пишутся с мягким знаком, а слова вилька, булька и тарелька – без. Это необъяснимо и надо просто запомнить!»

Отмечу ещё, что обрамление фигурными скобками аргументов ref-команд при записи в регистры %!0 и %!1, не являясь строго обязательным, тем не менее, весьма целесообразно, т. к. позволяет избегать ошибок в случаях, подобном приведённому ниже:

… label{h1} … label{h2} … label{1h} … label{h3} … ref{h1} … ref{h2} … label{h}

(если вместо конструкции \ref({*}) использовать \ref{(*)}, не включив { и } в затегированный фрагмент, то содержимое ссылок образует строку: h1h2, поиск по которой даст ложный результат об использовании меток с именами h и 1h). Это, правда, не избавляет нас ото всех возможных ошибок, так как аргументы меток и ссылок сами могут содержать (разумеется, парным образом) фигурные скобки (например, label{h{1}}). Для полного исключения недоразумений проще всего отказаться от использования фигурных скобок при именовании ссылок; если же вы успели создать огромный документ с неимоверным количеством ссылок, в именах которых присутствуют данные скобки, то без специального макроса, пожалуй, не обойтись.

Итак, изложенный здесь метод позволяет (с указанною выше оговоркой) обнаружить все случаи, когда какое-либо окружение, порождающее номер (например, equation) содержит неиспользуемые метки label. Но «лишний» номер может появиться и тогда, когда подобное окружение и вовсе не содержит label. К счастью, с использованием механизмов «продвинутого» поиска WinEdt легко обнаружить (поле Search for):

\begin{equation}(**)end{equation}x{FindInString("","label");}

и даже исправить (поле Replace with):

begin{equation*}end{equation*}

все такие недоразумения (для определённости рассмотрен случай упомянутого выше окружения equation).

Автор: Singul

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js