О чем эта статья
С чего начать
Раскрашиваем код программы
Обозначим цель и далее по порядку
Строим первые шаблоны.
Области программных модулей и директивы компилятору
Литералы
Имена процедур и функций, переменные и зарезервированные слова
Комментарии и описания методов (процедур и функций)
Остались только спец.символы
Обсудим полученный результат
Обещанное: список зарезервированных слов
Построитель регулярных выражений
Основные элементы управления
Текст
Найдены
Уникальные
Замены
Дерево
Параметры
Код 1С
Конструктор выражений
А дальше что?
Литература
Вложения
Обновления
О чем эта статья
Начну с того, что… я не люблю писать статьи. В последний раз, когда мне это хотелось сделать, а было это четверть века назад, я начитался эротических переживаний Эдуарда Тополя, возомнил себя этаким Дон Жуаном, и вознамерился явить миру собственные неповторимые впечатления этой стороны своей жизни. Но потом-таки отказался от сих злостных намерений, приняв непреложный тот факт, что слишком уж интимно это. Собственно, любой жизненный опыт, накопленный нами (вами) – это интимно. Потому что он скрывает немало наших ошибок и маленьких достижений. Это ценно для нас. Но он меркнет на фоне таких же достижений других людей. И не это главное. А главное то, что всегда найдутся добрые люди, которые не смогут его принять (понять), не промолчав об этом (и тут Остапа понесло!).
Таки да, это очередная статья о регулярных выражениях, о коих писалось уже немало на этом ресурсе. Зачем еще одна? Попытаюсь кратко ответить на этот вопрос.
Данная публикация, прежде всего, о построителе регулярных выражений. Впервые с ними (с выражениями) я столкнулся лет 6-7 назад в замечательной книге Андрея Михайлова «1С:Предприятие 7.7/8.0 Системное программирование» (2-е издание, 2007г.). Идея их использования в 1С мне понравилась уже тогда, но все это был лишь небольшой опыт, ограниченный написанием несложных регулярных выражений. И для этого ничего не требовалось кроме некоторого знания их синтаксиса и умения подключить в 1С объект VBScript.RegExp, присутствующий на любом компьютере.
Сложности начались потом, когда мне любопытно стало разобраться в некоторых задачах, требующих написания сложных регулярных выражений. Да простит меня читатель этих строк, но тогда я еще не имел привычки за каждым решением проблемы сразу же устремляться в интернет, а пытался что-то сделать своими руками . Ну и придумал простенький сборщик выражений. (Найти готовый?! Нет, нет, нет, - мы не ищем легких путей!). В основу положил их (выражений) параметрическое описание, как в примере ниже:
<Описание параметра> = (?:<ПС><Знач><ПС><Имя параметра><ПС>(?:=<ПС><Значение по умолчанию>){0,1})
<ПС> = \s*
<Знач> = (?:Знач)?
<Значение по умолчанию> = (?:<Булево>|<Число>|<Дата>|<Строка>|Неопределено)
<Имя параметра> = <Идентификатор>
<Строка> = "[^"]*"(?:"[^"]*")*(?:"[^"]*\s*?|[^"]*(?:"[^"]*")*")*?
<Число> = [-+]?\d+\.?\d*
<Булево> = (?:Истина|Ложь|True|False)
<Дата> = '[ 0-9.-/]*'
<Идентификатор> = [_А-Яа-яA-Za-z][_А-Яа-яA-Za-z0-9]*
На выходе получал собранное до кучи регулярное выражение:
(?:\s*(?:Знач)?\s*[_А-Яа-яA-Za-z][_А-Яа-яA-Za-z0-9]*\s*(?:=\s*(?:(?:Истина|Ложь|True|False)|[-+]?\d+\.?\d*|'[ 0-9.-/]*'|"[^"]*"(?:"[^"]*")*(?:"[^"]*\s*?|[^"]*(?:"[^"]*")*")*?|Неопределено)){0,1})
В дальнейшем, если регулярное выражение в начале или в конце имеет пробельные символы в своем составе,- заключаю его в парные символы „“, которые не являются частью выражений. Например, шаблон для поиска в тексте длинных промежутков из пробелов, минимум 5 символов: „ {5,}“. Понятное дело, наличие в этом шаблоне пробела в самом начале, не очевидно...
Попробуйте через несколько недель подсунуть это регулярное выражение мне, сонному, поутру и задайте вопрос: «что это?». Я буду долго чесать репу, мысленно отвлекаясь на тему нелегкого человеческого бытия во вселенной, пытаясь судорожно восстановить в воспаленном мозгу ответ на заданный вопрос. И то, что речь шла о разборе списка параметров процедуры, вспомню не сразу.
А хотелось бы это помнить всегда или хотя бы вспоминать, не напрягаясь сильно.
Последние несколько лет, работая иногда с регулярными выражениями, попутно я совершенствовал и свой построитель выражений. Что из этого получилось, хочу представить своим читателям.
К тому же, вот уж близко пенсия моя, - на горизонте, и обидно даже как-то унести сие великое творение с собой в могилу. А вдруг кому-нибудь еще будет полезным. Хоть тема все та же, но ведь и у музыкантов количество нот не велико, - всего лишь семь … вот и мне вздумалось сыграть.
С чего начать
Те из вас, кто уже давно имеет дело с регулярными выражениями, скорей всего имеют под рукой и удобный инструментарий для работы с ними. Но, раз уж вы заинтересовались этой статьей, в целях экономии своего времени, можете смело пропустить большую часть моего изложения и перейти к разделу «Построитель регулярных выражений». Но предупреждаю, что и при рассмотрении примеров я немало буду комментировать работу самого построителя. Так что на ваше усмотрение. Откровенно говоря, я сам не большой любитель читать руководства с примерами к программам.
Для тех из вас, кому это в новинку, я подобрал примеры, на которых расскажу о своем построителе и, по мере нарастания сложности их, плавно введу в мир регулярных выражений. При этом я буду избегать дублирования справочных руководств, отсылая читателей разве что к встроенной справке. Во вложении к статье вы также сможете найти небольшое руководство в виде chm-справки, а в конце статьи порекомендую литературу.
Потратив некоторые усилия на освоение регулярных выражений, мы непременно придем к выводу из заголовка статьи: «Регулярные выражения – это просто!». Не потому что не требуют каких-то предварительных усилий на проникновение в них, а потому что позволяют решать сложные задачи простыми методами. А к этим простым методам я и причисляю их самые, не смотря на столь отталкивающий вид.
Прежде всего, я рекомендую скачать и запустить в работу построитель выражений, представленный внешней обработкой. И начнем с нуля.
А можно, как вариант для особо нетерпеливых, загрузить уже полностью готовый пример на закладке «Код 1С», пункт контекстного меню в области текстового поля ввода «Прочитать из файла» (файл Код1С.rep), а потом «Включить в проект». И дальше методом научного втыка.
Раскрашиваем код программы
Обозначим цель и далее по порядку
Несколько слов о регулярных выражениях вообще. Что это такое? Это текстовый шаблон, согласно которому идет разбор текста. Конкретней далее: мы имеем текст, который подвергаем обработке, и шаблон, применяемый в процессе этой самой обработки к тексту. Получается:
- процессор сканирует текст символ за символом;
- применяет к нему шаблон с текущей позиции и далее по тексту, продвигаясь все дальше и дальше,пока есть соответствие шаблону;
- и в случае полного совпадения шаблону, выдает полученный результат в виде строки;
- а потом продолжает сканирование с места, на котором завершилось последнее полное соответствие шаблону.
Примерно так все происходит. Но если честно, в некоторых случаях я совершенно не понимаю, что происходит и остается лишь рассматривать полученный результат и принимать его на веру. Вот потому так важно иметь под рукой неплохой инструмент для быстрой отладки регулярных выражений.
Должен предупредить, что непосредственно алгоритмы 1С по раскраске программного кода здесь, в этой статье, представлены не будут. Но мы выполним всю предварительную работу по построению регулярного выражения и получим нечто такое:
(//[ \t]*(?:Процедура|Функция|Procedure|Function).*(?:\n[ \t]*//.*$)*)(?=\s*(?:(?:Процедура|Функция|Procedure|Function)|(?:^[ \t]*&.*$)))|(//.*$(?:\n[ \t]*//.*$)*)|(^[ \t]*#(?:Область|КонецОбласти).*$)|(^[ \t]*&.*$)|("[^"]*"(?:"[^"]*")*)|([-+]?\d+\.?\d*)|('[-./: 0-9]*')|((?:[_А-ЯЁа-яёA-Za-z][_А-ЯЁа-яёA-Za-z0-9]*)(?=(?:\s*\()))|((?:[_А-ЯЁа-яёA-Za-z][_А-ЯЁа-яёA-Za-z0-9]*))
С его помощью мы препарируем программный модуль 1С, выделив различные элементы, а именно, и в таком порядке:
- описания процедур и функций (в виде комментариев);
- комментарии (прочие, разные);
- области программных модулей;
- директивы компилятору;
- литералы: Число, Дата, Строка;
- имена процедур, функций;
- прочие идентификаторы переменных и/или зарезервированные слова;
- спецсимволы: ()[] =+-и др.
Выделенные элементы могут быть использованы в раскраске программного модуля, каждый своим цветом.
Фактически, я только что привел параметрическое описание регулярного выражения человеческим языком:
<Раскраска> = <Описание метода>|<Комментарий>|<Область>|<Директива компилятору>|<Литерал Строка>|<Литерал Число>|<Литерал Дата>|<Имя метода>|<Переменная или зарезервированное слово>
Немного позже мы занесем его в построитель, а пока что начнем конструирование с простых примеров.
Акцентирую так же ваше внимание на том, что это только пример и он не претендует на полную завершенность. Скорее, это демонстрация возможностей.
Строим первые шаблоны.
Перед вами окно построителя регулярных выражений:
По ссылке «Синтаксис регулярных выражений» завсегда можно получить короткую справку. Обращаясь к справке, рассмотрим предлагаемые ниже простейшие шаблоны, в дальнейшем они будут использованы при конструировании более сложных выражений.
В обл.1т вставим текст из прилагаемого файла модуль.txt. В обл.2 наберем с клавиатуры или скопируем через буфер обмена шаблон „//.*$“. Немного помедитировав над этим шаблоном с помощью справки, узнаем в этом шаблоне строку комментария, которая начинается с // и далее произвольный текст до конца строки. Как видите, начало строки „^“ мы не указали, ибо комментарий может быть начат с любой позиции строки текста.
После нажатия кнопки «Найти» получим результат поиска в обл.8, удовлетворяющий шаблону (см. картинка ниже):
Обратите внимание, что основная закладка Текст имеет две вложенные закладки (внизу):
- Текст, обл.1т – сюда мы вставляем текст, используемый при отладке регулярного выражения;
- Раскраска, обл.1р – здесь показывается полученный результат, будучи выделен цветом. Красным цветом все, что удовлетворяет шаблону; зеленым – выбранная двойным щелчком мыши строка в обл.8.
Обл.9 содержит тот же шаблон, что и в обл.2. Но это сейчас, когда мы рассматриваем наипростейший шаблон. А в дальнейшем, именно здесь можно будет подсмотреть полностью собранное до кучи сложное регулярное выражение.
Для дальнейшего, параметризуем это простенькое выражение, - выполним присвоение имен. Как правило, эти имена более громоздкие, чем самое выражение, но… они написаны человеческим языком, что наводит на мысли; заодно «документируют» проделанную работу. Спустя годы, вернись вы к рассмотрению этих же диких символьных конструкций, не будет значительных затруднений в их интерпретации (проверено на себе). Итак:
- давим кнопку обл.3;
- в открывшемся конструкторе выражений выделяем фрагмент „.*“ нашего выражения;
- вызываем контекстное меню, пункт «Присвоить имя значению»;
- вводим имя «Текст»;
- потом выделяем полученное после подстановки имени выражение, и таким же образом присваиваем имя «Строка комментария»
Получим это:
Немного полюбовавшись результатом, нажав кнопку «Сохранить», имеем в итоге выражение <Строка комментария> в обл.2, что приводит к тому же результату поиска:
Первые регулярные выражения в построителе мы получили, с помощью конструктора выражений. Причем, вызвали его, нажав кнопку обл.3. Если в обл.2 выражение есть ни-что-иное, как параметр (см. на картинке выше), можно получить доступ к редактированию значения этого параметра, нажав кнопку обл.4.
Давайте теперь занесем в построитель выражение раскраски. Для этого, сначала наберем выражение <Раскраска> (кнопка обл.3), а потом нажмем кнопку обл.4 и сформируем значение параметра <Раскраска> (разумеется, лучше скопировать в конструктор, а не набирать). Приведу еще раз текст выражения:
<Раскраска> = <Описание метода>|<Комментарий>|<Область>|<Директива компилятору>|<Литерал Строка>|<Литерал Число>|<Литерал Дата>|<Имя метода>|<Переменная или зарезервированное слово>
По окончании этого, можете на закладке «Параметры» просмотреть список всех параметров, подготовленных в процессе работы. Новые появляются в этом списке по нажатию любой из кнопок, запускающих в работу регулярное выражение - «Тест», «Найти», «Заменить». Нажмите «Тест» – параметров прибавится в список. (Построитель ругнется на пустые значения параметров, но мы это игнорируем.). Редактировать значения параметров можно уже отсюда, кликая мышью в колонке значений.
Другая возможность – на закладке «Дерево». Структуру текущего параметра в виде дерева можете получить нажатием кнопки обл.5. Мне представляется работа с параметрами из дерева наиболее удобной. Но некоторые операции возможны только из списка. По ходу ознакомитесь со всем этим.
А мы перейдем к редактированию новых параметров, их тестированию…
Области программных модулей и директивы компилятору
Область программного модуля, - указание редактору модулей:
- занимает целую строку от ^ и до $;
- начинается символом #, перед которым можно натыкать пробелов;
- а за ними или слово Область, обозначающее начало области, с последующим наименованием области;
- или слово КонецОбласти.
<Область> = (?:^<ПР>#(?:Область|КонецОбласти)<Текст>$)
<ПР> = [ \t]*
<Текст> = .*
Конструкция (?:…) группирует шаблон. <Текст> - это имя области, когда требуется. Параметром <ПР> обозначен пустой промежуток в строке, из пробелов и горизонтальных табуляций.
Значения определили и можем запустить в отладку выражение «Область», воспользовавшись контекстным меню, команда «Выполнить поиск». Смотрим картинку:
Директива компилятору теперь покажется совсем знакомой и простой:
- занимает целую строку от ^ и до $;
- начинается символом &, перед которым можно натыкать пробелов;
- а за ними, собственно, директива.
<Директива компилятору> = (?:^<ПР>&<Текст>$)
<ПР> = [ \t]*
<Текст> = .*
Литералы
<Литерал Число> = [-+]?\d+\.?\d*
<Литерал Дата> = '[-./: 0-9]*'
<Литерал Строка> = "[^"]*"(?:"[^"]*")*
Число: может иметь знак числа в начале, за которым следует как минимум одна цифра, а за ними могут оказаться десятичная точка с еще одной последовательностью цифр (дробная часть).
В выражении даты главное, пожалуй, это апострофы, по которым мы узнаем дату, а внутри апострофов винегрет из цифр и разделителей. Причем, наше выражение никак не определяет порядок следования их. Но для наших целей этого вполне достаточно.
Тем из вас, кто впервые имеет дело с регулярными выражениями, литерал типа Строка может показаться несколько сложным. Но давайте немного отформатируем выражение:
1. "[^"]*"
2. (?:"[^"]*")*
Первый фрагмент описывает строчные литералы как последовательность любых символов (кроме апострофов, но включая перенос на следующую строку \n), заключенную в кавычки, размещенную в одной или нескольких строках. Например:
А = "строчный литерал";
Б = "а это строчный литерал
|с переносом на следующую строку";
Второй фрагмент учитывает, что в составе литерала могут быть кавычки и они должны быть удвоены:
А = "строчный литерал""с апострофами";
Б = "строчный литерал""с апострофами и
|с переносом на следующую строку";
Имена процедур и функций, переменные и зарезервированные слова
<Идентификатор> = (?:[_А-ЯЁа-яёA-Za-z][_А-ЯЁа-яёA-Za-z0-9]*)
<Переменная или зарезервированное слово> = <Идентификатор>
<Имя метода> = <Идентификатор>(?=(?:<ПС>\())
<ПС> = \s*
Идентификаторы переменных и пр. сущностей в 1С устроены просто: начинаются с буквы или символа подчеркивания, а за ними могут следовать буквы, цифры, символ подчеркивания в любых сочетаниях, что и отражает шаблон для идентификатора.
Я специально не разделяю переменные и зарезервированные слова 1С, хотя и предполагается в дальнейшем их раскраска разными цветами. И вот почему. К глубокому прискорбию, мне выделить зарезервированные слова никак не удается по той простой причине, что в библиотеке VBScript.RegExp задекларированная граница слова \b распространяется только на слова, написанные буквами [A-Za-z]. Поэтому, пожелай я выделить только слово шаблоном „\bЕсли\b“ в тексте, ничего хорошего из этого не выйдет. И это не было бы так печально, если бы мы могли посмотреть, а что стоит перед этим словом. Посмотреть, что стоит за ним – можем (т.н. заглядывание вперед) ; что перед ним – увидеть возможности нет (заглядывание назад). Так что, выделяем зарезервированные слова вместе с именами переменных, а потом уж, имея список зарезервированных слов, разделяем их перед раскрашиванием текста. А вот как получить, не напрягаясь, перечисление всех зарезервированных слов, я расскажу немного ниже.
Зато мы можем легко отделить имена процедур и функций из всей массы идентификаторов, ибо, вслед за таким идентификатором следует открывающая скобка. И здесь приходит на помощь упомянутое выше заглядывание вперед. Вот эта конструкция:
<Имя метода> = <Идентификатор>
(?=
(?:
<ПС>\(
)
)
Идентификатор являет собой имя процедуры или функции тогда и только тогда, когда за ним следует открывающая скобка. Конструкция (? =…) как раз и позволяет посмотреть, что там следует за идентификатором. Но только посмотреть,- дальше разбор программного модуля идет с позиции, где закончился сам идентификатор.
В конструкции (? =…) мы указываем, что за идентификатором должна быть открывающая скобка „\(“ (экранируем!), а перед ней быть может любое сочетание пробельных символов (мало ли у кого какая фантазия при оформлении программного кода).
И напоследок рассмотрим самые сложные в этой статье...
Комментарии и описания методов (процедур и функций)
Под комментарием будем понимать последовательность строк комментариев. Эта последовательность может быть прервана или пустой строкой, или чем-то другим, что не является комментарием
<Комментарий> = (?:<Строка комментария>(?:\n<ПР><Строка комментария>)*)
<Строка комментария> = (?://<Текст>$)
<ПР> = [ \t]*
<Текст> = .*
<Строка комментария> была рассмотрена нами раньше. Если выполнить это выражение на нашем примере, получим три найденных результата:
В первом комментарии – 2 строки, во втором – 8 строк.
Но и это еще не все. Мы можем специального вида комментарии, используемые для описания процедур и функций, выделить отдельно.
<Описание метода> = (?://<ПР><Метод><Текст>(?:\n<ПР><Строка комментария>)*)(?=<ПС>(?:<Метод>|<Директива компилятору>))
<Метод> = (?:Процедура|Функция|Procedure|Function)
<Строка комментария> = (?://<Текст>$)
<ПР> = [ \t]*
<Текст> = .*
<Директива компилятору> = (?:&<Текст>)
Какие моменты отражены в этом выражении? Этот специальный комментарий, в первой строке после //, имеет одно из зарезервированных слов. Список слов вынесен отдельно под именем <Метод>. Далее в строке следует текст - краткое описание метода. Потом последовательность знакомых строк комментариев. Но… после такого специального комментария следует либо директива компилятору, либо описание заголовка метода опять-таки с зарезервированного слова <Метод>. Отформатируем для наглядности сие описание:
1. (?://<ПР><Метод><Текст>
(?:\n<ПР><Строка комментария>)*
)
2. (?=<ПС>
(?:<Метод>|<Директива компилятору>)
)
Первая группа – это собственно комментарий (с фиксированным началом); вторая – заглядывание вперед, что же следует за ним. Запустив на выполнение это выражение, получим один результат:
Остались только спец.символы
И никаких особенных действий для выделения их по тексту программного модуля предпринимать не будем.
Правда, остался открытым вопрос, все ли мы сделали правильно? Ничего не пропустили? Потому для контроля можно будет
- выполнить замену всех найденных вхождений строк на спецсимвол •;
- полученый результат скопировать в обл.1т построителя;
- и выполнить поиск символов на основании шаблона „[^•\s]+“, исключающего пробельные символы и символ подстановки.
Ожидаемый результат – это только спецсимволы в программном модуле:
Обсудим полученный результат
Обратим свои взоры в сторону дерева регулярного выражения. Можно развернуть его полностью в режиме просмотра. Ну, а я попросту распечатал его. Немного пошаманил над полученной распечаткой и получил такую картинку:
Дерево кроме наименований и значений параметров включает также колонки З(амещать) и И(звлечь). Как вы уже догадались, первая повелевает при сборке выражения делать подстановки значений параметров вместо наименований; вторая указывает на те параметры, соответствия которым необходимо извлечь во время обработки текстов. Но здесь есть одна неприятная особенность, связанная с реализацией построителя выражений. Имена этих извлекаемых параметров должны быть уникальны. Но… что мы видим на картинке? Я пытаюсь извлечь во время обработки программного модуля директивы компилятору, выставляю флажок напротив имени <Директива компилятору>, и автоматически он выставляется во всех других местах, где эта директива задействована, в данном случае, когда мы делаем заглядывание вперед в <Описание метода>. На картинке это видно…
Как я поступаю в таком случае? Попросту замещаю в таких критических точках имена параметров их значениями. В нашем случае, редактирую значение параметра <Описание метода>, и в его определении делаю замену параметра <Директива компилятора> его значением, - конструктор выражений позволяет это сделать легко.
Мы долго подбирались к конечному результату всех наших усилий, а именно, к программному коду 1С. За небольшими изменениями он может быть использован при программировании раскраски модулей. Особенностью его является не только то, что мы имеем удобочитаемый код, но и то, что комментарии в начале этого кода могут быть подгружены (при необходимости) в построитель регулярных выражений для последующих творческих изысканий.
// Выражение = <Раскраска>
//
// Левая скобка параметра = <
// Правая скобка параметра = >
// Шаблон имени = [А-ЯA-Z][-_ А-Яа-я0-9A-Za-z]*?
//
// +- <Раскраска> = <Описание метода>|<Комментарий>|<Область>|<Директива компилятору>|<Литерал Строка>|<Литерал Число>|<Литерал Дата>|<Имя метода>|<Переменная или зарезервированное слово>
// ++ <Область> = (^<ПР>#(?:Область|КонецОбласти)<Текст>$)
// ++ <Директива компилятору> = (^<ПР>&<Текст>$)
// ++ <Описание метода> = (//<ПР><Метод><Текст>(?:\n<ПР><Строка комментария>)*)(?=<ПС>(?:<Метод>|(?:^<ПР>&<Текст>$)))
// ++ <Комментарий> = (<Строка комментария>(?:\n<ПР><Строка комментария>)*)
// ++ <Литерал Строка> = ("[^"]*"(?:"[^"]*")*)
// ++ <Литерал Число> = ([-+]?\d+\.?\d*)
// ++ <Литерал Дата> = ('[-./: 0-9]*')
// ++ <Имя метода> = (<Идентификатор>(?=(?:<ПС>\()))
// ++ <Переменная или зарезервированное слово> = (<Идентификатор>)
// +- <ПС> = \s*
// +- <Метод> = (?:Процедура|Функция|Procedure|Function)
// +- <Идентификатор> = (?:[_А-ЯЁа-яёA-Za-z][_А-ЯЁа-яёA-Za-z0-9]*)
// +- <ПР> = [ \t]*
// +- <Текст> = .*
// +- <Строка комментария> = //<Текст>$
idТекст = ".*";
idИдентификатор = "(?:[_А-ЯЁа-яёA-Za-z][_А-ЯЁа-яёA-Za-z0-9]*)";
idПС = "\s*";
idСтрокаКомментария = "//" + idТекст + "$";
idМетод = "(?:Процедура|Функция|Procedure|Function)";
idПР = "[ \t]*";
idПеременнаяИлиЗарезервированноеСлово = "(" + idИдентификатор + ")";
idИмяМетода = "(" + idИдентификатор + "(?=(?:" + idПС + "\()))";
idЛитералДата = "('[-./: 0-9]*')";
idЛитералЧисло = "([-+]?\d+\.?\d*)";
idЛитералСтрока = "(""[^""]*""(?:""[^""]*"")*)";
idДирективаКомпилятору = "(^" + idПР + "&" + idТекст + "$)";
idОбласть = "(^" + idПР + "#(?:Область|КонецОбласти)" + idТекст + "$)";
idКомментарий = "(" + idСтрокаКомментария + "(?:\n" + idПР + idСтрокаКомментария + ")*)";
idОписаниеМетода = "(//" + idПР + idМетод + idТекст + "(?:\n" + idПР + idСтрокаКомментария + ")*)(?=" + idПС + "(?:" + idМетод + "|(?:^" + idПР + "&" + idТекст + "$)))";
idРаскраска = idОписаниеМетода + "|" + idКомментарий + "|" + idОбласть + "|" + idДирективаКомпилятору + "|" + idЛитералСтрока + "|" + idЛитералЧисло + "|" + idЛитералДата + "|" + idИмяМетода + "|" + idПеременнаяИлиЗарезервированноеСлово;
idPattern = idРаскраска;
RegExpLocal = Новый COMОбъект("VBScript.RegExp");
RegExpLocal.Multiline = Истина;
RegExpLocal.Global = Истина;
RegExpLocal.IgnoreCase = Истина;
RegExpLocal.Pattern = idPattern;
Найдено = RegExpLocal.Execute(ОбрабатываемыйТекст);
Для каждого Стр Из Найдено Цикл
Начало = Стр.FirstIndex;
Длина = Стр.Length;
Значение = Стр.Value;
vidОписаниеМетода = Стр.SubMatches(0);
vidКомментарий = Стр.SubMatches(1);
vidОбласть = Стр.SubMatches(2);
vidДирективаКомпилятору = Стр.SubMatches(3);
vidЛитералСтрока = Стр.SubMatches(4);
vidЛитералЧисло = Стр.SubMatches(5);
vidЛитералДата = Стр.SubMatches(6);
vidИмяМетода = Стр.SubMatches(7);
vidПеременнаяИлиЗарезервированноеСлово = Стр.SubMatches(8);
КонецЦикла;
Обещанное: список зарезервированных слов
Помнится, я стенал по поводу того, что затруднительно выделить зарезервированные слова одним регулярным выражением. Но полный перечень этих слов мы можем получить быстро и легко. Обратимся к справке 1С:Предприятие, Встроенный язык, Общее описание встроенного языка, Формат исходных текстов программных модулей, Формат программного модуля, Зарезервированные слова.
- через буфер обмена скопируем все зарезервированные слова в обл.1т построителя выражений
- используем для поиска зарезервированных слов <Идентификатор>
- на закладке «Уникальные» отсортируем список слов
- выделим все слова или только часть их
- вызовем контекстное меню, присвоим выделенным словам имя.
В результате, в списке параметров с указанным именем будет создано перечисление ключевых слов.
Для примера, я создал такие:
<Зарезервированные слова> = (?:<Зарезервированные слова русские>|<Зарезервированные слова английские>)
<Зарезервированные слова русские> = Цикл|Функция|Тогда|Процедура|Продолжить|Прервать|Попытка|Пока|По|Перем|Перейти|Новый|Не|КонецЦикла|КонецФункции|КонецПроцедуры|КонецПопытки|КонецЕсли|Каждого|Исключение|ИначеЕсли|Иначе|Или|Из|И|Если|Для|Выполнить|ВызватьИсключение|Возврат
<Зарезервированные слова английские> = While|Var|Try|To|Then|Return|Raise|Procedure|Or|Not|New|In|If|Goto|Function|For|Execute|Except|EndTry|EndProcedure|EndIf|EndFunction|EndDo|ElsIf|Else|Each|Do|Continue|Break|And
Построитель регулярных выражений
Обл.2 – проектируемое регулярное выражение. Рекомендуется представлять его одним параметром, как на картинке. Для выбора готовых выражений (параметров), наберите в этом поле один-два фрагмента его наименования и в сформированном списке выполните окончательный выбор.
Кнопки «Тест», «Найти», «Заменить» - основные операции с использованием регулярного выражения из обл.2. Разумеется, любую из них предваряет процедура сборки выражения из его параметрического описания. Готовое после сборки выражение можно посмотреть в обл.9.
Обл.3 – по нажатию этой кнопки открывается конструктор выражений для редактирования содержимого обл.2.
Далее, элементы управления в обл.4-7 используются только тогда, когда выражение в обл.2 - параметр.
Обл.4 - по нажатию этой кнопки открывается конструктор выражений для редактирования значения параметра из обл.2
Обл.5 – значение выражения в обл.2 представляется в виде дерева;
Обл.6 – переключение на список параметров, активизируется строка с параметром из обл.2
Обл.7 – Почти то же самое, что и кнопка «Найти» для текущего выражения в обл.2, но перед началом поиска в обл.1т подгружается отладочный текст, связанный с параметром (см. ниже).
Текст
На странице «Текст» имеются внизу две закладки: «Текст» И «Раскраска». С раскраской все понятно. А вот обл.1т, в которую вставляется текст для тестирования регулярных выражений, требует пояснений.
Кроме основных команд, связанных с текстовым полем, в контекстном меню есть еще две команды.
«Сохранить в буфер» - по выбору этой команды, отладочный текст «закрепляется» за параметром из обл.2.
«Восстановить из буфера» - этой командой «закрепленный» за параметром из обл.2 отладочный текст подгружается в текстовое поле обл.1т.
Восстановление отладочного текста выполняется и в других случаях:
- по нажатию кнопки обл.7, запускающей на выполнение выражение из обл.2;
- при запуске выражения на выполнение из дерева или списка параметров.
Найдены
На этой закладке представлен результат поиска по отладочному тексту согласно регулярного выражения из обл.2.
Двойной клик мыши в обл.8 осуществит подсветку найденной строки в раскраске обл.1р.
Обл.10 – это общее количество извлеченных SubMatches, в контексте этой обработки – количество извлеченных параметров выражения. Рекомендуется конструировать выражение таким образом, чтобы имена этих параметров были уникальны, не повторялись в выборке. Тогда, в обл.11 имеем перечисление всех выбранных параметров. При указании строки в обл.12 дается расшифровка имени найденного значения в обл.13.
Уникальные
На этой закладке представлены найденные уникальные значения параметров регулярного выражения с пометкой И(звлечь). Переключение между параметрами выполняется элементом обл.14. Числовые значения параметра соответствуют индексу параметра в SubMatches; имя параметра отображается в обл.15. Значению переключателя, равному -1, соответствует общий список найденных строк в обл.8. При активизации строки в обл.16, приводится список ссылок найденных значений в обл.17. Двойной клик в обл.17 осуществит подсветку найденной строки в раскраске обл.1р.
Из выделенных значений обл.16, посредством команды контекстного меню «Присвоить имя значению», можно создать перечисление в списке параметров под указанным именем. Пример здесь.
Замены
На этой закладке в обл.19 можно просмотреть результат замены найденных значений согласно указанному шаблону в обл.18.
Дерево
На этой закладке можно просмотреть древовидную структуру регулярного выражения. Переключатель обл.20 регулирует список выражений:
- текущее выражение, которое мы имеем в обл.2;
- основные выражения – это все наши проекты. Др. словами,параметры, не задействованные в описаниях других выражений
- все выражения, независимо от уровня иерархии
Кнопка обл.21 разворачивает полностью дерево в режиме просмотра.
Кнопка обл.22 сворачивает и возвращает дерево в режим редактирования.
Кнопка обл.23 обновляет дерево. Иногда, автоматическое обновление не выполняется. Например, после выбора параметра в обл.2.
Кнопка обл.24 переключает дерево между режимами просмотра-редактирования.
Из контекстного меню осуществляется запуск текущего выражения на выполнение: тестирование или поиск. При этом,
- текущее выражение устанавливается в обл.2,
- восстанавливается отладочный текст, связанный с выражением,
- а потом, собственно, выполняется команда «Тест» или «Найти».
Через контекстное меню вы можете так же переключиться на выражение в списке параметров.
И, разумеется, двойной клик мыши в колонке «Значение» запустит конструктор выражений на редактирование значения параметра.
Параметры
Из этого списка параметров, двойным кликом мыши в колонке «Значение» можно запустить конструктор выражений на редактирование значения параметра. Но так же представляют интерес и другие возможности, доступные только на этой закладке.
Мне представляется наиболее удобным использовать в качестве ограничительных скобок параметров – угловые. Но это не очень красиво выглядит, когда в обработке находятся HTML и/или XML документы, использующие эти же ограничители для тэгов. Так что, вы можете при необходимости ввести другие ограничители. Разумеется, во всех имеющихся выражениях, включая их варианты, будет выполнена замена ограничителей.
Шаблон имени носит скорее технический характер. Возможно, кто-то из вас пожелает его ужесточить, назначив таковым <Идентификатор>. Я постарался, по возможности, имена параметров нормализовать, устранив концевые пробелы и заменив последовательности пробелов одним; отследить уникальность имен параметров, но иногда это затруднительно сделать, когда в именах имеются пробелы, как сейчас. Но вот так исторически сложилось. И это не было у меня проблемой до сих времен, - надо быть лишь аккуратным в работе.
Иногда хочется иметь ограничитель в конце обрабатываемого текста. Здесь вы можете указать этот конец текста в виде последовательности символов. Во время обработки он будет добавлен в конец тестового текста.
Ряд операций доступен из контекстного меню.
"Выполнить тестирование", "Выполнить поиск":
- осуществляется запуск текущего выражения на выполнение: тестирование или поиск. При этом,
- текущее выражение устанавливается в обл.2,
- восстанавливается отладочный текст, связанный с выражением,
- а потом, собственно, выполняется команда «Тест» или «Найти».
"Переименовать параметр":
-введенное новое имя устанавливается во всех имеющихся выражениях , включая их варианты.
"Пометить на удаление параметр", "Удалить все параметры".
"Скопировать":
- осуществляет операцию копирования существующего параметра с присвоением ему другого (уникального) имени.
Код 1С
Во время сборки регулярного выражения формируется также и программный код 1С. Всегда с методом Execute. Должен признать это некоторой недоработкой, но пока что так.
Желательно использовать этот код совместно с комментариями, ибо это не только комментарии, но и «путь назад, в построитель». При необходимости, из комментариев можно полностью восстановить параметрическое выражение в построителе. Или в виде комментариев передать его кому-то другому, при условии, что получатель тоже использует этот же построитель выражений.
На этой закладке доступны несколько команд из контекстного меню.
«Заполнить весь проект»
В текстовое поле в виде комментариев записываются все параметры и их значения (без вариантов). Можно использовать для создания резервной копии выражений или для обмена.
«Включить в проект»
Параметры и их значения восстанавливаются из комментариев и заносятся в список параметров. Во время выполнения этой операции, сравниваются значения импортируемых параметров с уже имеющимися в списке параметров и в диалоговом окне предлагается осуществить выбор дальнейших действий. Если импортируемый параметр уже есть в списке параметров с другим значением, "старое" значение при импорте сохраняется в вариантах.
«Сохранить в файл», «Прочитать из файла»
В комментариях не нуждаются
Конструктор выражений
Предполагается, что конструктор выражений облегчит нам (вам) жизнь в процессе их построений. И набор кнопок в обл.1 тому будет порукой. Но как по мне, большая часть из них – от Лукавого. Никакого выигрыша в скорости проектирования выражений не дают, а только лишь выручают в случае склероза,- помогают вспомнить все доступные элементы шаблонов, не прибегая к справке. Но таковы традиции жанра.
По своему действию они немного различаются.
"Замшевые" кнопки позволяют вставить элемент шаблона в текущую позицию курсора окна редактирования выражения в обл.4. или заместить выделенный фрагмент выражения.
"Бирюзовые" либо вставляют "пустые" элементы, либо обрамляют загодя выделенные фрагменты выражения в обл.4. Это бывает удобно, ибо частенько приходится группировать элементы.
"Зеленая" кнопка вообще не есть элемент шаблона напрямую. Она обозначает пустой параметр с обрамляющими скобками (угловыми, по умолчанию). Активизируется комбинацией клавиш Shift+Ctrl+ПРОБЕЛ. В текущую позицию курсора в обл.4 вставляет угловые скобки и помещает фокус в обл.3 для набора имени параметра.
В обл.3 мы также можем попасть по нажатию Ctrl+ПРОБЕЛ, без вставки угловых скобок.
Имя параметра или его значение можно вставить из списка параметров в обл.2. Двойной клик мыши выбирает имя. Прочие действия возможны из контекстного меню. Полагаю, в комментариях не нуждаются. Быструю фокусировку списка обеспечивает комбинация Ctrl+Alt+ПРОБЕЛ.
Во время редактирования выражения в обл.4, выделенным фрагментам можно присваивать имена. Таким нехитрым образом создаются новые параметры. После чего помещаются в конец списка обл.2 на зеленом фоне. Замечу, что это не единственный способ. Можно ведь попросту включать новые параметры в состав выражения, а потом уже из дерева или из списка редактировать их значения.
Для замены параметра в выражении его значением, сначала установите курсор в область имени параметра (или выделите его полностью), а потом только вызовите контекстное меню с командой «Заменить имя значением».
Я воспользовался тем простым фактом, что символы "Табуляция" и "Перевод строки" в шаблоне регулярного выражения вы не встретите (разве что их обозначения \t и \n). И потому использовал их для удобного форматирования выражений. Можно вставлять их в выражение где угодно, и сколько угодно, лишь бы было удобно для обзора.
В любое время это "форматирование" можете выключить кнопкой "Удалить форматирование", и включить кнопкой "Восстановить форматирование".
Есть автоформатирование. В большинстве случаев срабатывает красиво. Иногда дает сбои, но руки нам по что? На картинке ниже приведен пример автоматического форматирования, воспринимается визуально много легче, чем в одну строку.
При нажатии кнопки "Сохранить", естественно, все форматирующие символы удаляются из шаблона регулярного выражения, но восстанавливаются, когда вы опять приступаете к работе в конструкторе выражений.
В процессе набора имен в обл.3, предлагается список выбора уже существующих параметров. Обновляется в процессе набора текста в обл.3. В позицию курсора в обл.4, после выбора из этого динамичного списка, вставляется
- либо имя параметра;
- либо его значение, если строка в списке помечена слева жирной точкой.
Процесс проектирования регулярных выражений мне зачастую напоминает танец с бубном: пока получишь окончательный вариант, - перепробуешь не один из них. Поэтому, возможность сохранять наработанные "интересные" варианты может показаться не лишней.
Всякий раз, когда вы заходите на страницу "Варианты", - создается вариант (измененного!) выражения (если его еще нет в списке). "Интересные" варианты можно
- поименовать в списке обл.5;
- просмотреть в обл.6;
- прокомментировать в обл.7;
- пометить как "архивированный" нажатием кнопки в обл.8. и этой же кнопкой исключить из архива;
- и, разумеется, выбрать для дальнейших модификаций на закладке "Редактор" из списка обл.5.
Надеюсь, что этого краткого описания конструктора выражений будет достаточно, чтобы начать работу. А навыки потом уже какие-то выработаются в процессе.
А дальше что?
Уже подхожу к завершению темы. Простите, сам не знал, что это мне будет стоить стольких буков. Но таки осмелюсь еще написать вкратце о планах, применительно к этой работе.
Я давно уже смотрю масляными глазками на внешнюю компоненту А.Орефкова, RexV8. Хотелось бы опробовать эту более мощную альтернативу VBScript.RegExp. А значит, появится еще один конструктор выражений в составе этой обработки.
И еще... как у всех программистов, по завершении работы. Многое хотелось бы изменить в том, что сделано. В будущем. Всего более напрягает, что наработанные тяжким трудом регулярные выражения хранятся не где-нибудь, а в настройках обработки. Не раз было так, что сижу полусонный, медитирую над очередным выражением, творю; параллельно программирую эту самую обработку, а потом- БАЦ... запускаю изменения на тестирование и... нет уже тех выражений, ибо не было нормального завершения программы. Правда, вам это не грозит, почти... И то, что имеется возможность выгрузки параметров в файл,- лишь частичное решение проблемы.
Да и вообще, увидеть все в форме внешней обработки для меня стоило множества слез, когда я все собирал до кучи. Душу разрывало на части расставание с куда более выразительными кнопочками, при переходе на внешнюю обработку. Видите ли, картинки на кнопки не повесить кроме тех, что есть в библиотеке картинок (стандартной). Или я что-то упустил в процессе познания.
И вообще, мне видится эта обработка в составе конфигурации. Разумеется, конфигурации для программиста. Такая уже есть, жив буду - летом закончу полностью и опубликую. И в нее, в перспективе, включу и обработку по работе с регулярными выражениями. И будут они в справочниках записаны, а настройки выражений в регистрах сведений. И исчезнут некоторые вскользь упомянутые выше перекосы...
Литература
- Михайлов А., 1С:Предприятие 7.7/8.0 Системное программирование
- Фридл Д., Регулярные выражения
- Гойвертс Я., Регулярные выражения. Сборник рецептов
- Фицджеральд М., Регулярные выражения. Основы
- Форта Б., Регулярные выражения. 10 минут на урок
Вложения
Во вложении к этой статье, в архивном файле, вы найдете все упомянутое в статье, а именно
- коротенькую chm-справку по регулярным выражениям;
- текст программного модуля, использованный в демонстрациях;
- программный код 1С для раскраски;
- файл внешней обработки.
Особенности: используются модальные и синхронные вызовы.
Желаю приятного просмотра!
Обновления
14.03.2017 13:00 - Косметические исправления по форме. Перезалито.
15.03.2017 16:30 - Не регулировалась высота текстового поля обл.4 в конструкторе выражений. Исправлено. Перезалито.
24.03.2017 09:30 - Слегка приглажен код. Добавлено оповещение пользователям об изменении публикации (на форме гиперссылка Статья на infostart.ru). Настройка оповещений описана в статье