В данном случае используется пять классов:
- Компонент – класс символов, указывающий на обозначение компонента, к которому относится ревизия.
- Версия – класс символов, определяющий номер версии.
- Знак – класс, обозначающий признак изменения: нововведение, дополнение или исправление.
- Тикет – класс, указывающий на номер тикета.
- Описание – сам текст описания изменения.
:component:Как видно, первую строку занимает название компонента. На следующей строке находится номер версии, причем она может быть указана, а может не быть указана. Третья и последующие строки – это описание изменений.
(:version:)?
(:mark: (:ticket:)? :description:)+
Теперь конкретизируем класс «знак». Знак обозначает следующее:
- Нововведение. Обозначается так: “[*]”.
- Дополнение. Обозначается так: “[+]”.
- Исправление. Обозначается так: “[!]”.
В соответствии с этим конкретизируем класс «знак» в соответствии с языком регулярных выражений:
:component:Результат получается довольно простой: сперва открывается квадратная скобка, затем идет один из трех знаков, а после – закрывающая квадратная скобка.
(:version:)?
(\[\*\+\!\] (:ticket:)? :description:)+
Теперь опишем регулярное выражение версии:
:component:Как видно, версия – это четыре последовательности цифр (точнее – четыре числа), каждое из которых отделяется от соседних символом точки. Крайние числа при этом от пустоты слева и от следующих далее классов справа точками, естественно, не отделяются. Это стандартный шаблон написания номера версии, например, как “1.9.8.50”.
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\] (:ticket:)? :description:)+
Теперь необходимо подробно описать номер тикета – что это такое и как обозначается. Для этого опишем его языком регулярных выражений:
:component:Видно, что номер тикета – это знак «номер» и последующие за ним цифры. Например, “#650”. Также видно, что тикет может быть указан, а может не быть указан.
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\] (#\d+)? :description:)+
Далее конкретизируем само описание изменения. Описание изменения – это довольно сложное выражение. Дело в том, что каждое новое изменение записывается с новой строки, а описание этого изменения может содержать какие угодно символы, но только не перевод строки. С одной стороны, можно пользоваться этим:
:component:С другой стороны, можно использовать «ленивые» квантификаторы:
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\] (#\d+)? [^\n]*\n)+
([^\n]+)\nНеизвестно, какой из двух способов лучше, скорее всего – второй.
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\] (#\d+)? .*?\n)+
Теперь остается конкретизировать имя компонента – самая легкая задача напоследок. Имя компонента стоит в первой строчке, и сразу после имени идет знак перевода строки. Сам компонент может называться какими угодно символами, но не переводом строки, поэтому опишем его так:
([^\n]+)\nВ соответствии с предыдущим примером описания языком регулярных выражений сообщения к изменению, можно описать «ленивым» квантификатором и имя компонента:
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\] (#\d+)? [^\n]*\n)+
.+?\nНу а теперь осталось расставить пробелы там, где они должны быть:
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\] (#\d+)? .*?\n)+
.*?\nВ одну строку выражение запишется следующим образом:
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\]\s(#\d+)?\s.*?\n)+
.*?\n(\d+\.\d+\.\d+\.\d+)?\n(\[\*\+\!\]\s(#\d+)?\s.*?\n)+