7.01.2008

Описание входных данных. Часть первая. Описание входных данных для комментариев к ревизии.

Сначала определим классы символов. Классы – это определенные последовательности, которые имеют неизменную структуру и всегда подходят под один шаблон.

В данном случае используется пять классов:
  • Компонент – класс символов, указывающий на обозначение компонента, к которому относится ревизия.
  • Версия – класс символов, определяющий номер версии.
  • Знак – класс, обозначающий признак изменения: нововведение, дополнение или исправление.
  • Тикет – класс, указывающий на номер тикета.
  • Описание – сам текст описания изменения.
Все эти классы сами по себе поддаются строгой структуризации и располагаются также строго структурированным образом:
:component:
(:version:)?
(:mark: (:ticket:)? :description:)+
Как видно, первую строку занимает название компонента. На следующей строке находится номер версии, причем она может быть указана, а может не быть указана. Третья и последующие строки – это описание изменений.

Теперь конкретизируем класс «знак». Знак обозначает следующее:
  • Нововведение. Обозначается так: “[*]”.
  • Дополнение. Обозначается так: “[+]”.
  • Исправление. Обозначается так: “[!]”.

В соответствии с этим конкретизируем класс «знак» в соответствии с языком регулярных выражений:
:component:
(:version:)?
(\[\*\+\!\] (:ticket:)? :description:)+
Результат получается довольно простой: сперва открывается квадратная скобка, затем идет один из трех знаков, а после – закрывающая квадратная скобка.

Теперь опишем регулярное выражение версии:
:component:
(\d+\.\d+\.\d+\.\d+)?\n
(\[\*\+\!\] (:ticket:)? :description:)+
Как видно, версия – это четыре последовательности цифр (точнее – четыре числа), каждое из которых отделяется от соседних символом точки. Крайние числа при этом от пустоты слева и от следующих далее классов справа точками, естественно, не отделяются. Это стандартный шаблон написания номера версии, например, как “1.9.8.50”.

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

Далее конкретизируем само описание изменения. Описание изменения – это довольно сложное выражение. Дело в том, что каждое новое изменение записывается с новой строки, а описание этого изменения может содержать какие угодно символы, но только не перевод строки. С одной стороны, можно пользоваться этим:
: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)+