Вы находитесь на странице: 1из 33

МИНОБРНАУКИ РОССИИ

Федеральное государственное бюджетное образовательное учреждение


высшего образования
«МИРЭА – Российский технологический университет»
РТУ МИРЭА

Институт кибербезопасности и цифровых технологий


Кафедра КБ-14 «Цифровые технологии обработки данных»

КУРСОВОЙ ПРОЕКТ (РАБОТА)


по дисциплине
«Технологии программирования»
наименование дисциплины

Тема курсового проекта (работы) «Регулярные выражения C#»

Студент группы БСБО-11-21 Клюшников М.С

(учебная группа) Фамилия И.О.

Руководитель курсового проекта (работы) Сачков Е.В.

должность Доцент , ученая степень к.т.н. Фамилия И.О.

Работа представлена к защите «__»______ 20__г._______________


подпись студента

Допущен к защите «__»______ 20__г._______________


подпись руководителя

Москва 2022 г.
Содержание
Введение 3
1. Теоретические основы грамматики по регулярным выражениям 5
1.1. Сущность регулярных операций и выражений 5
1.2. Свойства регулярных языков 8

1.3. Взаимосвязь регулярных множеств, регулярных грамматик и конечных


автоматов 10

1.3.1 Построение конечного автомата на основе леволинейной


грамматики 11

2. Практика создания грамматики по регулярным выражениям и


построение дерева вывода 14

2.1. Состав и синтаксис регулярных выражений 14


2.2. Пример построения конечного автомата на основе заданной
грамматики 16
2.3. Разработка программы грамматики по регулярным выражениям и
построение дерева вывода 19
2.4. Описание грамматики входного языка в форме Бэкуса-Наура 20

Заключение 22
Список литературы 23
Приложение 24

2
Введение
Регулярные выражения — это формальный язык для поиска и
обработки подстрок в тексте на основе метасимволов (подстановочных
знаков). Собственно, это строка-шаблон (англ. pattern, по-русски обычно
именуемый «шаблон», «маска»), состоящая из символов и метасимволов и
задающая правила поиска.
Регулярные выражения совершили прорыв в обработке текстов в конце
20-го века. Набор утилит, включенных в дистрибутивы UNIX, включая
редактор sed и фильтр grep, был одним из первых, кто популяризировал
регулярные выражения для обработки текстов. Многие современные языки
программирования имеют встроенную поддержку регулярных выражений.
Среди них ActionScript, Perl, Java, PHP, JavaScript, языки .NET Framework,
Python, Tcl, Ruby, Lua, Gambas, C++ (стандарт 2011 г.) и другие.
Некоторые текстовые редакторы и утилиты используют регулярные
выражения для поиска и замены текста. Например, с помощью регулярных
выражений вы можете указать шаблоны, позволяющие выполнять следующие
действия:
- Найти все последовательности символов "кошка" в любом контексте,
например: "кошка", "котлета", "терракота";
- найти одно слово «кошка» и заменить его на «кошка»;
- Найдите слово «персидский» или «чеширский» перед словом
«кошка»;
- Удалите из текста все предложения, в которых упоминается слово кот
или кошка.
Регулярные выражения также позволяют указывать более сложные
шаблоны поиска или замены.Целью курсовой работы является
рассмотрение процесса создания грамматики по регулярным выражениям и
построение дерева вывода.

3
Предметом курсовой работы является совокупность теоретических и
практических аспектов механизма создания грамматики по регулярным
выражениям.
Объектом курсовой работы являются регулярные выражения в языке
С#.
Задачами курсовой работы является:
- рассмотрение теоретических основ создания регулярных выражений;
- анализ синтаксиса регулярных выражений;
- практика создания грамматики по регулярным выражениям и
построения дерева вывода.
Структура курсовой работы. Курсовая работа состоит из введения,
двух глав, заключения и списка литературы.

4
1. Теоретические основы грамматики по регулярным выражениям

1.1. Сущность регулярных операций и выражений


Рассмотрим особый класс операций языка — обычные операции.
Набор языков, полученный из базового языка в результате применения
ограниченного числа регулярных операций, является регулярным набором.
Способом их описания являются регулярные выражения и
недетерминированные конечные автоматы, которые позволяют создавать
цепочки из этих наборов.
Определение регулярного набора
Давайте определим конкатенацию и итерацию по набору строк
символов в букве V следующим образом:
РQ - конкатенация PV* и QV*: PQ, = {pq | pP, qQ.};
Р* - Итерация PV*: Р* = {р* | pP}.
Тогда для алфавита V регулярное множество определяется рекурсивно:
1.  - Общие настройки.
2. {} - Регулярный набор.
3. {а} - регулярное множество aV.
4. Если Р и Q, — любые регулярные множества, то множества PQ, PQ
и Р* также являются регулярными множествами.
5. Ничто другое не является штатным набором.
По сути, регулярные наборы — это наборы строк заданного алфавита,
построенные определенным образом (с помощью операций объединения,
конкатенации и итерации). Все регулярные языки являются регулярными
множествами.
Существует три основных способа указания обычного языка:
- регулярный (праволинейный и леволинейный) синтаксис,
- Конечные автоматы (КА) и
- Регулярные наборы (и регулярные выражения для их представления).
Регулярные языки в принципе можно определить и другими способами,
но эти три наиболее интересны.
5
Показано, что все три метода в равной степени могут быть
использованы для определения регулярных языков. Для них можно написать
следующие утверждения:
Утверждение 1. Язык является регулярным множеством тогда и только
тогда, когда он задается леволинейной (праволинейной) грамматикой.
Утверждение 2. Язык может быть определен леволинейной
(праволинейной) грамматикой тогда и только тогда, когда он является
регулярным множеством.
Утверждение 3. Язык является регулярным множеством тогда и только
тогда, когда он задан конечным автоматом.
Предложение 4. Язык распознается конечным автоматом тогда и только
тогда, когда он является регулярным множеством.
Все три способа определения регулярного языка эквивалентны.
Некоторые алгоритмы позволяют регулярному языку, определенному одним
указанным способом, построить другой способ определения того же языка.
Это не всегда относится к другим способам определения обычных языков.
свойства регулярных выражений
Регулярные множества можно обозначать с помощью регулярных
выражений. Эти обозначения вводятся следующим образом:

1. 0 - регулярное выражение, обозначающее ∅.

2. λ - регулярное выражение, обозначающее {λ}.

3. а - регулярное выражение, обозначающее {a} ∀a∈V.

4. Если р и q - регулярные выражения, обозначающие регулярные


множества Р и Q, то p+q, pq, р* - регулярные выражения, обозначающие

регулярные множества P∪Q, PQ и Р* соответственно.

6
Два регулярных выражения α и β равны, α = β, если они обозначают

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

Если α, β и γ - регулярные выражения, то свойства регулярных

выражений можно записать в виде следующих формул:

1. γ+αα* = γ+α*α = α*

2. α+β = β+α.

3. α+(β+γ) = (α+β)+γ

4. α(β+γ) = αβ+αγ

5. (β+γ)α = βα+γα

6. α(βγ) = (αβ)γ

7. α+α = α

8. α+α* = α*

7
9. λ+α* = α*+λ = a*

10. 0* = λ

11. 0α = α0 = 0

12. 0+α = α+0 = α

13. λα = αλ = α

14. (α*)* = α*

Все эти свойства можно легко доказать, основываясь на теории


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

отсутствует равенство αβ = βα, то есть операция конкатенации не обладает

свойством коммутативности. Это и не удивительно, поскольку для этой


операции важен порядок следования символов [1].

1.2. Свойства регулярных языков


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

8
Например, обычные языки отключаются при следующих
действиях:-пересечения;
-объединения;
-дополнения;
-итерации;
-конкатенации;
-гомоморфизма (изменения имен символов и подстановки цепочек
вместо символов).
Поскольку регулярные множества замкнуты относительно операций
пересечения, объединения и дополнения, то они представляют булеву алгебру
множеств. Существуют и другие операции, относительно которых замкнуты
регулярные множества. Вообще говоря, таких операций достаточно много.
Регулярные языки представляют собой очень удобный тип языков. Для
них разрешимы многие проблемы, неразрешимые для других типов языков.
Например, доказано, что разрешимыми являются следующие
проблемы.
Проблема эквивалентности. Даны два регулярных языка L1(V) и L2(V).
Необходимо проверить, являются ли эти два языка эквивалентными.
Проблема принадлежности цепочки языку. Дан регулярный язык L(V) и

цепочка символов α∈V*. Необходимо проверить, принадлежит ли цепочка

данному языку.
Проблема пустоты языка. Дан регулярный язык L(V). Необходимо
проверить, является ли этот язык пустым, то есть найти хотя бы одну цепочку

α≠λ, такую что α∈L(V).

Эти проблемы разрешимы вне зависимости от того, каким из трех


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

9
доказать разрешимость любой из этих проблем хотя бы для одного из
способов представления языка, тогда для остальных способов можно
воспользоваться алгоритмами преобразования, рассмотренными выше.
(Возможны и другие способы представления регулярных множеств, а для них
разрешимость указанных проблем будет уже не очевидна.)
Для регулярных грамматик также разрешима проблема однозначности -
доказано, что для любой регулярной грамматики можно построить
эквивалентную ей однозначную регулярную грамматику. Это очевидно,
поскольку для любой регулярной грамматики можно однозначно построить
регулярное выражение, определяющее заданный этой грамматикой язык.
Существует простой метод проверки, является или нет заданный язык
регулярным. Этот метод основан на проверке так называемой леммы о
разрастании языка. Доказано, что если для некоторого заданного языка
выполняется лемма о разрастании регулярного языка, то этот язык является
регулярным; если же лемма не выполняется, то и язык регулярным не
является.
Лемма о разрастании для регулярных языков формулируется
следующим образом: если дан регулярный язык и достаточно длинная
цепочка символов, принадлежащая этому языку, то в этой цепочке можно
найти непустую подцепочку, которую можно повторить сколь угодно много
раз, и все полученные таким способом новые цепочки будут принадлежать
тому же регулярному языку. (Если найденную подцепочку повторять
несколько раз, то исходная цепочка как бы «разрастается» - отсюда и
название «лемма о разрастании языков»).

Формально эту лемму можно записать так: если дан язык L, то ∃

константа р > 0, такая, что если α∈L и |α| ≥ р, то цепочку α можно записать в

виде α = δβε, где 0 < |β| < р, и тогда α' = δβiε, α'∈L ∀i ≥ 0.

10
Используя лемму о разрастании регулярных языков, докажем, что язык
L = {аnЬn | n > 0} не является регулярным.
Предположим, что этот язык регулярный, тогда для него должна

выполняться лемма о разрастании. Возьмем некоторую цепочку этого языка α

= аnЬn и запишем ее в виде α = δβε. Если β∈a+ или β∈b+ то тогда для i = 0

цепочка δβ0ε = δε не принадлежит языку L, что противоречит условиям

леммы; если же β∈a+b+, тогда для i = 2 цепочка δβ2ε = δββε не принадлежит

языку L. Таким образом, язык L не может быть регулярным языком [2].

1.3. Взаимосвязь регулярных множеств, регулярных грамматик и


конечных автоматов
Регулярные выражения и регулярные грамматики связаны между собой
следующим образом:
-для любого регулярного языка, заданного регулярным выражением,
можно построить регулярную грамматику, определяющую тот же язык;
-для любого регулярного языка, заданного регулярной грамматикой,
можно получить регулярное выражение, определяющее тот же язык.
Регулярные выражения и недетерминированные конечные автоматы
связаны между собой следующим образом:
-для любого регулярного языка, заданного регулярным выражением,
можно построить конечный автомат, определяющий тот же язык;
-для любого регулярного языка, заданного конечным автоматом, можно
получить регулярное выражение, определяющее тот же язык.
Известен алгоритм, реализующий построение конечного автомата по
регулярному выражению. Алгоритм построения регулярного выражения по
конечному автомату здесь не представляет интереса, поскольку проще

11
построить грамматику, эквивалентную заданному конечному автомату, а
потом уже найти регулярное выражение для заданного грамматикой языка.
Связь регулярных грамматик и конечных автоматов
На основе регулярной грамматики можно построить эквивалентный ей
конечный автомат и, наоборот, для заданного конечного автомата можно
построить эквивалентную ему регулярную грамматику.
Это очень важное утверждение, поскольку регулярные грамматики
используются для определения лексических конструкций языков
программирования. Создав автомат на основе известной грамматики, мы
получаем распознаватель для лексических конструкций данного языка. Таким
образом, удается решить задачу разбора для лексических конструкций языка,
заданных произвольной регулярной грамматикой. Обратное утверждение
также полезно, поскольку позволяет узнать грамматику, цепочки языка
которой допускает заданный автомат.
Для построения конечного автомата на основании известной
грамматики и для построения грамматики на основании данного конечного
автомата используются достаточно простые алгоритмы.
Все языки программирования определяют нотацию записи «слева
направо». В той же нотации работают и компиляторы. Поэтому далее
рассмотрены алгоритмы для леволинейных грамматик [4].

1.3.1 Построение конечного автомата на основе леволинейной


грамматики
Пусть имеется леволинейная грамматика G(VT,VN,P,S), необходимо

построить эквивалентный ей конечный автомат M(Q,V,δ,qo,F).

Прежде всего для построения автомата исходную грамматику G


необходимо привести к автоматному виду. Известно, что такое
преобразование можно выполнить для любой регулярной грамматики.
Алгоритм преобразования к автоматному виду был рассмотрен выше,
поэтому здесь на данном вопросе останавливаться нет смысла. Можно
12
считать, что исходная грамматика G уже является леволинейной автоматной
грамматикой.

Тогда построение конечного автомата M(Q,V,δ,qo,F) на основе

грамматики G(VT, VN, P, S) выполняется по следующему алгоритму.


Шаг 1. Строим множество состояний автомата Q, Состояния автомата
строятся таким образом, чтобы каждому нетерминальному символу из
множества VN грамматики G соответствовало одно состояние из множества
Q автомата М. Кроме того, во множество состояний автомата добавляется
еще одно дополнительное состояние, которое будем обозначать Н. Сохраняя
обозначения нетерминальных символов грамматики G, для множества
состояний автомата М можно записать:

Q=VN∪{H}.

Шаг 2. Входным алфавитом автомата М является множество


терминальных символов грамматики G: V = VT.
Шаг 3. Просматриваем все множество правил исходной грамматики.

Если встречается правило вида A→t∈P, где A∈VN, t∈VT, то в функцию

переходов δ(H,t) автомата М добавляем состояние A: A∈δ(H,t).

Если встречается правило вида A→Bt∈P, где A,B∈VN, t∈VT, то в

функцию переходов δ(B,t) автомата М добавляем состояние A: A∈δ(B,t).

Шаг 4. Начальным состоянием автомата М является состояние Н: qo =


Н.
Шаг 5. Множество конечных состояний автомата М состоит из одного
состояния. Этим состоянием является состояние, соответствующее целевому
символу грамматики G: F = {S}.
На этом построение автомата заканчивается.
13
Построение леволинейной грамматики на основе конечного автомата

Имеется конечный автомат M(Q, V, δ, qo, F), необходимо построить

эквивалентную ему леволинейную грамматику G(VT, VN, P, S).


Построение выполняется по следующему алгоритму.
Шаг 1. Множество терминальных символов грамматики G строится из
алфавита входных символов автомата М: VT = V.
Шаг 2. Множество нетерминальных символов грамматики G строится
на основании множества состояний автомата М таким образом, чтобы
каждому состоянию автомата, за исключением начального состояния,
соответствовал один нетерминальный символ грамматики: VN = Q\{qo}.
Шаг 3. Просматриваем функцию переходов автомата М для всех
возможных состояний из множества Q для всех возможных входных
символов из множества V.

Если имеем δ(A,t) = ∅, то ничего не выполняем.

Если имеем δ(A,t) = {B1,B2,...,Bn}, n >0, где A∈Q, ∀n≥i≥0: Bi∈Q, t∈V,

тогда для всех состояний Вi выполняем следующее:

добавляем правило Bi→t во множество Р правил грамматики G, если А

= qo;

добавляем правило Bi→At во множество Р правил грамматики G, если

A ≠ qo.

Шаг 4. Если множество конечных состояний F автомата М содержит


только одно состояние F = {F0}, то целевым символом S грамматики G
становится символ множества VN, соответствующий этому состоянию: S =
Fo; иначе, если множество конечных состояний F автомата М содержит более
одного состояния F = {F1, F2,...,Fn}, n>1, тогда во множество
14
нетерминальных символов VN грамматики G добавляется новый

нетерминальный символ S: VN = VN∪{S}, а во множество правил Р

грамматики G добавляются правила: S→F1 | F2 | ... | Fn.

На этом построение грамматики заканчивается [5].

15
2. Практика создания грамматики по регулярным выражениям и
построение дерева вывода

2.1. Состав и синтаксис регулярных выражений


Регулярные выражения состоят из констант и операторов, которые
определяют множества строк и множества операций на них соответственно.
На данном конечном алфавите Σ определены следующие константы:
(пустое множество) ∅.
(пустая строка) ε обозначает строку, не содержащую ни одного
символа. Эквивалентно «».
(символьный литерал) «a», где a - символ алфавита Σ.
и следующие операции:
(сцепление, конкатенация) RS обозначает множество {αβ | α ∈ R & β ∈
S}. Например, {"boy", "girl"}{"friend", "cott"} = {"boyfriend", "girlfriend",
"boycott", "girlcott"}.
(дизъюнкция, чередование) R|S обозначает объединение R и S.
Например, {"ab", "c"}|{"ab", "d", "ef"} = {"ab", "c", "d", "ef"}.
(замыкание Клини, звезда Клини) R* обозначает минимальное
надмножество множества R, которое содержит ε и замкнуто относительно
конкатенации. Это есть множество всех строк, полученных конкатенацией
нуля или более строк из R. Например, {"Go", "Russia"}* = {ε, "Go", "Russia",
"GoGo", "GoRussia", "RussiaGo", "RussiaRussia", "GoGoGo", "GoGoRussia",
"GoRussiaGo", …}.
Синтаксис
Большинство символов в регулярном выражении представляют сами
себя за исключением специальных символов [ ] \ / ^ $ . | ? * + ( ) { }, которые
могут быть предварены символом \ (обратная косая черта) («экранированы»,
«защищены») для представления их самих в качестве символов текста.
Можно экранировать целую последовательность символов, заключив её
между \Q и \E.

16
Таблица 2.1
Пример синтаксиса.
Пример Соответствие
a\.? a. или a
a\\\\b a\\b
a\[F\] a[F]
\Q+-*/\E +-*/

Аналогично могут быть представлены другие специальные символы


(набор символов, требующих экранирования, может отличаться в
зависимости от конкретной реализации). Часть символов, которые в той или
иной реализации не требуют экранирования (например, угловые скобки < >),
могут быть экранированы из соображений удобочитаемости.
Метасимвол . (точка) означает один любой символ, но в некоторых
реализациях исключая символ новой строки.
Набор символов в квадратных скобках [ ] именуется символьным
классом и позволяет указать интерпретатору регулярных выражений, что на
данном месте в строке может стоять один из перечисленных символов. В
частности, [абв]задаёт возможность появления в тексте одного из трёх
указанных символов, а [1234567890] задаёт соответствие одной из цифр.
Возможно указание диапазонов символов: например, [А-Яа-я] соответствует
всем буквам русского алфавита, за исключением букв «Ё» и «ё».
Если требуется указать символы, которые не входят в указанный набор,
то используют символ ^ внутри квадратных скобок, например [^0-9] означает
любой символ, кроме цифр.
Некоторые символьные классы можно заменить специальными
метасимволами, которые представлены в таблице 2.2:

17
Таблица 2.2.
Замена некоторых символьных классов специальными метасимволами.
Символ Эквивалент Соответствие
\d [0-9] Цифровой символ.
\D [^0-9] Нецифровой символ.
\s [ \f\n\r\t\v] Пробельный символ.
\S [^ \f\n\r\t\v] Непробельный символ.
Буквенный или цифровой символ или знак
\w [[:word:]]
подчеркивания.
Любой символ, кроме буквенного или цифрового
\W [^[:word:]]
символа или знака подчеркивания.

Следующие символы, которые представлены в таблице 2.3 позволяют


спозиционировать регулярное выражение относительно элементов текста:
начала и конца строки, границ слова [5].

Таблица 2.3.
Сиволы, позволяющие спозиционировать регулярное выражение
относительно элементов текста.
Представ Прим
Позиция Соответствие
ление ер
^ Начало строки ^a aaa aaa
$ Конец строки a$ aaa aaa
a\b aaa aaa
\b Граница слова
\ba aaa aaa
\B Не граница слова \Ba\B aaa aaa
Предыдущий aaa aaa (поиск остановился на 4-й
\G \Ga
успешный поиск позиции - там, где не нашлось a)

2.2. Пример построения конечного автомата на основе заданной


грамматики
Рассмотрим грамматику G({"a", "(", "*", ")", "{", "}"}, {S.C.K}, Р,
S) (символы а, (, *, ), {, } из множества терминальных символов грамматики
взяты в кавычки, чтобы выделить их среди фигурных скобок, обозначающих
само множество):

18
Р:S → С*) | К}

С → (* | Са | С{ | С} | С( | С* | С)

К →. { | Ка | К( | К* | К) | К{

Это леволинейная регулярная грамматика. Она описывает множество


комментариев языка Паскаль. Как было показано выше, ее можно
преобразовать к автоматному виду.
После преобразования получим леволинейную автоматную грамматику
следующего вида: G'({"a", "(", "*",.")", "{", "}"}, {S, D, C, E, K}, P', S):

Р':S → E) | К}

D →C*

С → D* I Са | C{ | C} | C( | C* | C)

E→(

К → { | Ка | K( | K* | K) | K{

Построим конечный автомат M(Q,V,δ,qo,F), эквивалентный указанной

грамматике.

Шаг 1. Построим множество состояний автомата: Q = VN∪{H}= {S, E,

C, D, K, H}.
Шаг 2. В качестве алфавита входных символов автомата берем
множество терминальных символов грамматики. Получаем: V = {"а", "(", "*",
")", "{", "}"}.
Шаг 3. Рассматриваем множество правил грамматики.

19
Для правил S → Е) | К} имеем δ(Е,")") = {S}; δ(K,"}") = {S}.

Для правила Е → С* имеем δ(С,"*") = {Е}.

Для правил С → D* | Са | С{ | С} | С( | С* | С) имеем δ(D,"*") = {С};

δ(С,"а") = {С}; δ(С,"{") = {С}; δ(С, "}") = {С}; δ(С,"(") = {С}; δ(С, “*”) =

{Е.С}; δ(С, ")") = {С}.

Для правила D → ( имеем δ(Н, "(") = {D}.

Для правил К → { | Ка | К( | К* | К) | К{ имеем δ(Н, "{") = {К}; δ(К, "а")

= {К}; δ(К, “(") = {К}; δ(К,"*") = {К}; δ(К, ")") = {К}; δ(К, "{") = {К}.

Шаг 4. Начальным состоянием автомата является состояние qo = Н.


Шаг 5. Множеством конечных состояний автомата является множество
F = {S}.
Выполнение алгоритма закончено.
В итоге получаем автомат M({S.E,C,D,K,H}, {"а", "(", "*", ")",."{", "}"}.

δ, Н, {S}) с функцией переходов:

δ(Н, "{") = {K}δ(H, "(") = {D} δ(К, "а") = {К} δ(К, "(") = {К}

δ(К, "*") = {К} δ(К, ")") = {К} δ(К, "{“) = {К} δ(К, ")") = {S}

δ(D, "*") = {С} δ(С, "а") = {С} δ(С, "{") = {С} δ(C, "}") = {С}

δ(С, "(") = {С} δ(С, "*") = {Е, С}δ(С, ")") = (С) δ(Е, “)”) = {S}

Граф переходов этого автомата изображен на Рис. 1.

20
a, (,*,),{,}
Рис. 2.1.1. – Недетерминированный КА для языка комментариев в Borland
Pascal.
Это недетерминированный конечный автомат, поскольку существует
состояние, в котором множество, получаемое с помощью функции переходов
по одному и тому же символу, имеет более одного следующего состояния.

Это состояние С и функция δ(С,"*") = {Е,С}.

Моделировать поведение недетерминированного КА - непростая


задача, поэтому можно построить эквивалентный ему детерминированный
КА. Полученный таким путем КА можно затем минимизировать.
В результате всех преобразований получаем детерминированный

конечный автомат М'({S.E,С,D, К,Н}, {"а", "(", "*", ")", "{", "}"} .δ', H, {S}) с

функцией переходов:

δ'(Н, "{") = {К} δ'(Н, “(") = {D} δ'(К, "а") = {К} δ'(К, "(") = {К} δ'(К,

"*") = {К} δ'(К, ")") = {К}

δ'(К, "{") = {К} δ'(К, "}") = {S) δ'(D, "*") = {С} δ'(С, "а") = {С} δ'(С,

“{“) = {С} δ'(С, "}") = {С}

δ'(С, "(") = {С} δ'(С, ")") = {С} δ'(С, "*") = {Е} δ'(Е, “а") = {С} δ'(E, “{“)

= {С} δ'(Е, "}") = {С}

21
δ'(Е, "(") = {С} δ'(Е, "*") = {Е} δ'(Е, ")") = {S}

Граф переходов этого автомата изображен на рис. 2.

а, (,),{,}
Рис. 2.1.2. – Детерминированный КА для языка комментариев в Borland
Pascal.

На основании этого автомата можно легко построить распознаватель. В


данном случае мы можем получить распознаватель для двух типов
комментариев языка Программирования Borland Pascal, если учесть, что а
может означать любой алфавитно-цифровой символ, кроме символов (, *, ), {,
} [6].

2.3. Разработка программы грамматики по регулярным выражениям и


построение дерева вывода
Составим пример грамматики по регулярным выражениям и попробуем
построить дерево вывода. Терминальные символы выделены жирным
шрифтом. Вместо символа a должны подставляться лексемы.
Дана грамматика вида:
S®T<T | T>T | T<=T | T>=T
E®a*a | a/a | a
T®T+E | T-E | E
Допустимые лексемы входного языка: идентификаторы, целые
десятичные числа со знаком [7].

22
2.4. Описание грамматики входного языка в форме Бэкуса-Наура
Грамматика для распознавания идентификаторов:
Грамматика для распознавания целых десятичных чисел со знаком:
Грамматика для лексического анализатора получается объединением
этих 2-х грамматик.
Множества крайних правых и крайних левых символов с указанием
шагов построения представлена в таблице 2.4.1:

Таблица 2.4.1
Множества крайних правых и крайних левых символов с указанием
шагов построения.
Символ Шаг 1 Последний шаг
(U) L(U) R(U) L(U) R(U)
S T T TEa TEa
T TE E TEa Ea
E a a a a

Таблица 2.4.2
Множества крайних правых и крайних левых терминальных символов
Символ Шаг 1 Шаг 2
(U) Lt(U) Rt(U) Lt(U) Rt(U)
S < > <= >= < > <= >= < > <= >= +-a < > <= >=+-a
T +- +- +-a +-a
E a a a a

Таблица 2.4.3
Матрица предшествования для грамматики
Символ < > + - * / <= >= a
⊥к
< = = =
> = = =
+ > > < >

23
- > > < >
* =
/ =
<= = = =
>= = = =
a = = >
< < <
⊥н

Пример выполнения разбора предложения


Входная цепочка: a+a<a

{ ⊥к a+a<a; ⊥н; ∅} ⎟п { ⊥к+a<a; ⊥н a; ∅} ⎟c {⊥к+a<a; ⊥нE; 10} ⎟п {⊥к

a<a;

⊥нE+; 10} ⎟п {⊥к<a; ⊥н E+a;10} ⎟с {⊥к<a; ⊥н E+Е;10,10} ⎟с {⊥к<a; ⊥н

E;10,10,5} ⎟п {⊥к a; ⊥н E<;10,10,5} ⎟п {⊥к; ⊥н E<a;10,10,5} ⎟c {⊥к; ⊥н

E<E;10,10,5,10} ⎟с {⊥к; ⊥н E; 10,10,5,10,1}

Дерево вывода представлено на Рис. 2.3.1:

Рис. 2.3.1 – Дерево вывода.


Текст программы представлен в Приложении [8].

24
Заключение
Так, в ходе рассмотрения курсовой работы мы пришли к выводу, что
регулярные выражения представляют удобный способ задания регулярных
множеств. Аналогично множествам, они определяются рекурсивно:
-регулярные базисные выражения задаются символами и определяют
соответствующие регулярные базисные множества, например, выражение f
задает одноэлементное множество {f} при условии, что f - символ алфавита
T;
-если p и q - регулярные выражения, то операции объединения,
конкатенации и итерации - p+q, pq, p*, q* - являются регулярными
выражениями, определяющими соответствующие регулярные множества.
По сути, регулярные выражения - это более простой и удобный способ
записи регулярных множеств в виде обычной строки. Каждое регулярное
множество, а, следовательно, и каждое регулярное выражение задает
некоторый язык L(T) в алфавите T.
Этот класс языков - достаточно мощный, с его помощью можно описать
интересные языки, но устроены они довольно просто - их можно определить
также с помощью простых грамматик, например, правосторонних грамматик.
Более важно, что для любого регулярного выражения можно построить
конечный автомат, который распознает, принадлежит ли заданное слово
языку, порожденному регулярным выражением. На этом основана
практическая ценность регулярных выражений.
С точки зрения практика регулярное выражение задает образец поиска.
После чего можно проверить, удовлетворяет ли заданная строка или ее
подстрока данному образцу. В языках программирования синтаксис
регулярного выражения существенно обогащается, что дает возможность
более просто задавать сложные образцы поиска.
Такие синтаксические надстройки, хотя и не меняют сути регулярных
выражений, крайне полезны для практиков, избавляя программиста от
ненужных сложностей.
25
26
Список литературы

1. Мельников С. В. Perl для профессиональных программистов. Регулярные


выражения. - М.: «Бином», 2007.
2. Смит, Билл. Методы и алгоритмы вычислений на строках (regexp) =
Computing Patterns in Strings. - М.: «Вильямс», 2006.
3. Форта, Бен. Освой самостоятельно регулярные выражения. 10 минут на урок
= Sams Teach Yourself Regular Expressions in 10 Minutes. - М.: «Вильямс»,
2005.
4. Фридл, Дж. Регулярные выражения. - СПб.: «Питер», 2001.
5. Ян Гойвертс, Стивен Левитан Регулярные выражения. Сборник рецептов. -
СПб.: «Символ-Плюс», 2010.
6. Фридл, Дж. Регулярные выражения = Mastering Regular Expressions. — СПб.:
«Питер», 2001. — 352 с.
7. Смит, Билл. Методы и алгоритмы вычислений на строках (regexp) =
Computing Patterns in Strings. — М.: «Вильямс», 2006. — 496 с.
8. Форта, Бен. Освой самостоятельно регулярные выражения. 10 минут на урок
= Sams Teach Yourself Regular Expressions in 10 Minutes. — М.: «Вильямс»,
2005. — 184 с.
9. Ян Гойвертс, Стивен Левитан. Регулярные выражения. Сборник рецептов =
Regular Expressions: Cookbook. — СПб.: «Символ-Плюс», 2010. — 608 с.
10.Мельников С. В. Perl для профессиональных программистов. Регулярные
выражения. — М.: «Бином», 2007. — 190 с.
11.Майкл Фицджеральд. Регулярные выражения. Основы. — М.: «Вильямс»,
2015. — 144 с.

27
Приложение
program Project1;

{$APPTYPE CONSOLE}

uses
SysUtils;

label
X,Y,Z,W,TX,AX;
var
a,i,j,k,count,c,n,g,b,bb,aa,ii:integer;
fin:file of char;
m:real;
fout:text;
ch,del,umn,minus,plus:char;
slovo:array[1..30] of string;
tip:array[1..20] of integer;
tip_:array[1..20] of string;
tmp,tmp_:string;
dl_tmp:integer;
s:string;
t:array[1..20] of string;
tt:array[1..20] of string;
e:array[1..20] of string;
a_:array[1..20] of string;
term:array[1..30] of char;
term_:array[1..30] of char;
nn:array[1..20] of integer;
begin
{ TODO -oUser -cConsole Main : Insert code here }

a:=0;writeln('‡ ¤ ­ЁҐ:');writeln;
writeln('Vipolnit leksicheskii analiz vxodnogo teksta,postroyit tablitsy
leksem');
writeln('I vipolnit sintaksicheskii razbor teksta po zadannoi grammatike s
postroeniem dereva ');
writeln('а §Ў®а .');
writeln('');
writeln('Zadannaya grammatika);writeln('');
writeln('S->T<T|T>T|T<=T|T>=T');
writeln('T->T+E|T-E|E');
writeln;
writeln('Vypolnili studenty VM-31 Petrov,Sorokin');
28
writeln('');
writeln('!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
writeln('Vhodnoi fail dolzhen nahoditsya na diske C:\spo3_in.txt');
writeln('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
writeln;readln;
assign(fin,'C:\spo3_in.txt');reset(fin);
for i:=1 to 30 do
slovo[i]:='';
j:=0;
i:=1;
c:=1;
a:=1;
ii:=1;
count:=0;
s:='';n:=0;
while not eof(fin) do
begin read(fin,ch);
s:=s+ch;n:=n+1;
Y: if(((ch>='a')and(ch<='z'))or((ch>='A')and(ch<='Z')))
then begin if(a=0)then i:=i+1;slovo[i]:=slovo[i]+ch;a:=1;tip[i]:=1;end
else if(ch='+')
then begin i:=i+1;slovo[i]:=slovo[i]+ch;a:=0;tip[i]:=3;end
else if(ch='-')
then begin i:=i+1;slovo[i]:=slovo[i]+ch;a:=0;tip[i]:=4;end
else if(ch='*')
then begin i:=i+1;slovo[i]:=slovo[i]+ch;a:=0;tip[i]:=5;end
else if(ch='/')
then begin i:=i+1;slovo[i]:=slovo[i]+ch;a:=0;tip[i]:=6;end
else if((ch='<')or(ch='>'))
then begin i:=i+1;slovo[i]:=slovo[i]+ch;a:=0;tip[i]:=7;end
else if(ch='=')
then begin slovo[i]:=slovo[i]+ch;a:=0;end
else if(ch='(')
then begin
i:=i+1;tip[i]:=2;
read(fin,ch);s:=s+ch;n:=n+1;a:=0;
slovo[i]:=slovo[i]+ch;
X:read(fin,ch);s:=s+ch;n:=n+1;
if((ch>='0')and(ch<='9'))
then begin slovo[i]:=slovo[i]+ch;goto X;end
else goto Y;
end
else if(ch=')')
then begin end
29
else begin j:=1;goto Z;end;
end;
close(fin);
for j:=1 to i do
begin
if(tip[j]=1)then tip_[j]:='identificator';
if(tip[j]=2)then tip_[j]:='celoe 10_noe chislo so znakom';
if(tip[j]=3)then tip_[j]:='znak slozheniya';
if(tip[j]=4)then tip_[j]:='znak vichitaniya';
if(tip[j]=5)then tip_[j]:='znak ymnozheniya';
if(tip[j]=6)then tip_[j]:='znak deleniya';
if(tip[j]=7)then tip_[j]:='znak neravenstva';
end;
c:=0;
assign(fout,'C:\spo3_out.txt');rewrite(fout);
writeln(fout,'');writeln(fout,'Tablica lecsem');
writeln(fout,'N Lecsema Tip lecsemi');
for j:=1 to i do
writeln(fout,j,' ',slovo[j],' ',tip_[j]);
writeln(fout,'');writeln(fout,'Derevo vivoda:');
tmp:='';
for j:=1 to 20 do
begin a_[j]:='';t[j]:='';tt[j]:='';e[j]:='';end;
aa:=1;
for j:=1 to n do
begin
if((s[j]='<')or(s[j]='>'))
then begin
for i:=1 to j-1 do
t:=t+s[i];
term:=s[j];
for i:=j+1 to n do
tt:=tt+s[i];
c:=1;
end
else if(s[j]='=')
then begin term:='=';delete(t,1,1);end;
end;
if(c=0)then begin j:=2;goto Z;end;
tmp:='T'+term+term+'T -> ';
write(fout,'S -> ',tmp);

b:=1;a:=1;bb:=0;aa:=3;
TX:
30
nn[b]:=length(t[b]);
if(nn[b]>0)then
begin
for i:=nn[b] downto 1 do
begin
if((t[b][i]='+')or(t[b][i]='-'))
then begin
if(t[b][i-1]<>'(')then
begin
for g:=i+1 to nn[b] do
e[a]:=e[a]+t[b][g];
term[aa]:=t[b][i];
for g:=1 to i-1 do
t[b+1]:=t[b+1]+t[b][g];
bb:=1;break;
end;end;
end;
if(bb=0)then begin e[a]:=t[b];delete(tmp,1,1);tmp:='E'+tmp;bb:=0;end
else begin delete(tmp,1,1);tmp:='T'+term[aa]+'E'+tmp;bb:=0;end;
a:=a+1;
end;
nn[b]:=length(tt[b]);
if(nn[b]>0)then
begin
for i:=nn[b] downto 1 do
begin
if((tt[b][i]='+')or(tt[b][i]='-'))
then begin
if(tt[b][i-1]<>'(')then
begin
for g:=i+1 to nn[b] do
e[a]:=e[a]+tt[b][g];
aa:=aa+1;
term[aa]:=tt[b][i];
for g:=1 to i-1 do
tt[b+1]:=tt[b+1]+tt[b][g];
bb:=1;break;
end;end;
end;
dl_tmp:=length(tmp);
for g:=dl_tmp downto 1 do
if(tmp[g]='T')then
begin k:=g;break;end;
delete(tmp,k,1);
31
if(bb=0)then begin insert('E',tmp,k);e[a]:=tt[b];write(fout,tmp);bb:=0;end
else begin
tmp_:='T'+term[aa]+'E';insert(tmp_,tmp,k);write(fout,tmp);bb:=0;end;
a:=a+1;
end;

begin
for g:=1 to length(e[ii]) do
begin
if((e[ii][g]='*')or(e[ii][g]='/'))
then begin
for i:=1 to g-1 do
a_:=a_+e[ii][i];
term_:=e[ii][g];
for i:=g+1 to length(e[ii]) do
a_:=a_+e[ii][i];
bb:=1;break;
end;
end;
for g:=1 to length(tmp) do
if(tmp[g]='E') then
begin delete(tmp,g,1);break;end;
if(bb=0)then begin
insert('a',tmp,g);bb:=0;
end
else begin
tmp_:='a'+term_+'a';
insert(tmp_,tmp,g);bb:=0;
end;
ii:=ii+1;
for g:=1 to length(e[ii]) do
begin
if((e[ii][g]='*')or(e[ii][g]='/'))
then begin
for i:=1 to g-1 do
a_:=a_+e[ii][i];
term_:=e[ii][g];
for i:=g+1 to length(e[ii]) do
a_:=a_+e[ii][i];
bb:=1;break;
end;
end;
for g:=1 to length(tmp) do
if(tmp[g]='E') then
32
begin delete(tmp,g,1);break;end;
if(bb=0)then begin
insert('a',tmp,g);bb:=0;
end
else begin
tmp_:='a'+term_+'a';
insert(tmp_,tmp,g);bb:=0;
end;
c:=0;ii:=ii+1;
end;

if((length(t[b])>0)or(length(tt[b])>0))
then
begin b:=b+1;goto TX;end;
for g:=length(tmp) downto 1 do
begin
if(tmp[g]='>')then begin delete(tmp,g-1,10);break;end;
end;
write(fout,tmp,'-> ',s);

close(fout);
Z:if(j=1)
then writeln('Leksicheskaya oshibka!');
if(j=2)
then readln;
end.
end.

33

Вам также может понравиться