![]() |
|
сделать стартовой | добавить в избранное |
![]() |
Компьютеры, Программирование
Программное обеспечение
Синтаксический разбор строк и конечные автоматы |
Андрей Боровский В этой статье речь пойдет о том, как анализировать информацию, переданную в виде последовательности символов (строку) и выделять из нее значимые элементы. Мы рассмотрим сравнительно простые ситуации, с которыми программистам приходится сталкиваться при решении самых разных задач: разбор выражений с простой синтаксической структурой, но с довольно свободными правилами записи. Допустим, в программе, которую вы пишете, нужен модуль, анализирующий текст H ML-страницы. Мы напишем функцию, которая, получив строку, содержащую тэг, извлекала бы из этой строки все атрибуты тэга и их значения. Структуру тэга можно схематично представить следующим образом: &l ;ТЭГ атрибут1 = "значение" атрибут2 = "значение" .> На первый взгляд задача кажется очень простой, однако ситуация осложняется из-за достаточно мягких правил языка H ML. Между именем атрибута, знаком равенства и значением может стоять любое число разделительных символов (пробелов, символов табуляции и даже символов перехода на новую строку), или же разделительные символы могут вообще отсутствовать. Значения атрибутов могут быть либо заключены в кавычки, либо нет, при этом значение, заключенное в двойные кавычки, может содержать символы одинарных кавычек, и наоборот. Кроме того, не всем атрибутам тэгов присваиваются значения. Для решения указанной проблемы мы напишем функцию Parse ag, анализирующую переданный ей тэг и создающую списки атрибутов тэга и их значений. Функция Parse ag действует по принципу конечного автомата. Конечные автоматы и подобные им структуры широко применяются при обработке строк. Сферы наиболее частого применения конечных автоматов включают поиск подстрок по заданному образцу, обработку регулярных выражений (regular expressio s), лексический и синтаксический анализ. Конечные автоматы широко применяются в трансляторах и интерпретаторах (не говоря уже о таких задачах, как проектирование логических устройств). Строгое определение конечных автоматов можно найти в любом учебнике по теории алгоритмов, мы же здесь ограничимся интуитивным определением. В каждый данный момент времени конечный автомат может находиться в одном из возможных состояний (число состояний, в которых может находиться конечный автомат – конечно). Автомат последовательно считывает символы входного текста (строки). Каждый считанный символ либо переводит автомат в новое состояние, либо оставляет его в прежнем состоянии. Формально автомат можно описать при помощи функции переходов. Аргументами этой функции являются предыдущее состояние автомата и очередной считанный символ, а значением – новое состояние автомата. Множество состояний для нашего автомата включает: Read ag – читает имя тэга; Wai A r – ожидает имя атрибута; Wai A rOrEq – ожидает имя атрибута или символ '='; ReadA r – читает имя атрибута; Wai Value – ожидает значение атрибута; ReadValue – читает значение атрибута без кавычек; ReadValueSQ – читает значение атрибута в одинарных кавычках; ReadValueDQ – читает значение атрибута в двойных кавычках. Следуя терминологии конечных автоматов, мы можем назвать состояния Wai A r, Wai A rOrEq, ReadA r и ReadValue допускающими.
Это означает, что если после обработки переданной строки автомат находится в каком-либо другом состоянии, значит, тэг содержит ошибку (автомат не проверяет, завершается ли строка символом '>', это – задача блока, вызывающего функцию Parse ag). Процесс программной реализации автомата можно упростить, построив для него диаграмму переходов. Далее приводится диаграмма переходов для нашего автомата. Цифры на диаграмме соответствуют номерам состояний, перечисленных выше. Рисунок 1 Пояснения к диаграмме: a - символ-разделитель b - любой символ кроме разделителя c - символ "=" d - любой символ кроме разделителя и символа "=" e - любой символ кроме разделителя и кавычек f - символ одинарных кавычек g - символ двойных кавычек Ниже приводится текст функции Parse ag и вспомогательной функции Ge SubS ri g. У функции Parse ag есть четыре параметра: строка, содержащая тэг, заключенный в '&l ;' и '>', строка, в которой возвращается имя тэга, и объекты типа S ri gLis , содержащие имена и значения атрибутов соответственно. Если данному атрибуту не сопоставлено никакое значение, в списке значений имени атрибута соответствует пустая строка. В случае успешного выполнения функция возвращает значение 0, в противном случае – 1. Автомат реализован в теле цикла функции Parse ag. Добавление нового элемента в список осуществляется в момент перехода из состояния ReadXXX в какое-либо другое состояние. Кроме этого в цикл добавлена проверка ошибок синтаксиса, например, двух символов '=', следующих подряд. После завершения цикла мы анализируем состояния автомата. Если автомат находится в одном из состояний ReadXXX, происходит добавление последнего элемента в соответствующий список. Если автомат не находится ни в одном из допускающих состояний, функция возвращает сообщение о синтаксической ошибке. fu c io Ge SubS ri g(co s S : S ri g; S ar , S op : I eger): S ri g; begi Se Le g h(Resul , S op-S ar ); Move(S, S op-S ar ); e d; fu c io Parse ag(co s ag : S ri g; var ag ame : S ri g; A rs, Values : S ri gLis ): I eger; ype // Возможные состояния S a e = (Read ag, Wai A r, Wai A rOrEq, ReadA r, Wai Value, ReadValue, ReadValueSQ, ReadValueDQ); co s // Значения, возвращаемые функцией Ge Li k resOK = 0; // разбор прошел успешно resBadSy ax = -1; // синтаксическая ошибка // Набор возможных разделительных символов Delime ers = ; var S a e : S a e; S ar Pos, i : I eger; begi Resul := resOK; // очищаем список элементов A rs.Clear; Values.Clear; S a e := Read ag; // входное состояние автомата i := 2; // пропускаем символ '&l ;' while ( ag&l ;>'>') a d (i&l ;Le g h( ag)) do begi case S a e of Read ag: if ag i Delime ers he begi // чтение имени тэга закончено ag ame := Ge SubS ri g( ag, S ar Pos, i); S a e := Wai A r; e d; Wai A r: if ( ag i Delime ers) = False he begi if ag = '=' he begi Resul := resBadSy ax; Exi ; e d; S ar Pos := i; S a e := ReadA r; e d; ReadA r: if ( ag = '=') he begi // чтение имени атрибута закончено, добавляем имя атрибута в список A rs.A
dd(Ge SubS ri g( ag, S ar Pos, i)); if ag = '=' he S a e := Wai Value else S a e := Wai A rOrEq; e d; Wai A rOrEq: if ( ag i Delime ers) = False he begi if ag = '=' he S a e := Wai Value else begi // начинается чтение имени атрибута // предыдущему атрибуту не присвоено никаких значений, // добавляем пустую строку в список Values Values.Add(''); S a e := ReadA r; S ar Pos := i; e d; e d; Wai Value: if ( ag i Delime ers) = False he begi if ag = '=' he begi // два символа '=' подряд Resul := resBadSy ax; Exi ; e d; if ag = '"' he begi // чтение значения начнется со следующего символа после кавычек: S ar Pos := i 1; S a e := ReadValueDQ; e d else if ag = '''' he begi // чтение значения начнется со следующего символа после кавычек: S ar Pos := i 1; S a e := ReadValueSQ; e d else begi // чтение значения без кавычек S ar Pos := i; S a e := ReadValue; e d; e d; ReadValue: if ag i Delime ers he begi // чтение значения закончено Values.Add(Ge SubS ri g( ag, S ar Pos, i)); S a e := Wai A r; e d; ReadValueDQ: if ag = '"' he begi // чтение значения в двойных кавычках закончено Values.Add(Ge SubS ri g( ag, S ar Pos, i)); S a e := Wai A r; e d; ReadValueSQ: if ag = '''' he begi // чтение значения в одинарных кавычках закончено Values.Add(Ge SubS ri g( ag, S ar Pos, i)); S a e := Wai A r; e d; e d; // case S a e of I c(i); e d; // while (Body&l ;>'>') a d (i&l ;Le g h( ag)) do // проверяем состояние автомата после обработки строки // последним символом строки должен быть '>' case S a e of ReadValue : Values.Add(Ge SubS ri g( ag, S ar Pos, i)); ReadA r : A rs.Add(Ge SubS ri g( ag, S ar Pos, i)); Read ag : ag ame := Ge SubS ri g( ag, S ar Pos, i); Wai A r, Wai A rOrEq: ; // ничего не делаем else Resul := resBadSy ax; // другие состояния недопустимы e d; e d; Одной из важных особенностей такого подхода к разбору строк является то, что анализ выполняется по мере считывания символов, с использованием информации о текущем символе и символах, прочитанных ранее. Это позволяет вести обработку данных, передающихся по некоторому последовательному каналу, непосредственно в процессе их поступления. Фактически представленная функция выполняет две операции: выделяет в переданной строке синтаксические элементы ( oke s) и определяет, что представляет собой данный элемент (имя тэга, имя атрибута, значение атрибута). Решение о том, чем является следующий элемент, принимается заранее, на основании данных о предыдущем элементе и простых правил: за именем тэга следует имя атрибута; за именем атрибута следует либо имя атрибута, либо символ '='; за символом '=' следует значение атрибута.
Этот шаблон по всей видимости, должен оказаться самой быстрой реализацией регулярных выражений, поскольку весь код подставляется компилятором как inline и, соответственно, компилятор может качественно оптимизировать код. Прямая работа с любыми видами строк (вид строки задается в качестве параметра шаблона) также повышает производительность. Реализации регулярных выражений различаются, однако в целом они очень похожи друг на друга, и, как правило, программист, однажды освоивший использование регулярных выражений, в дальнейшем практически не встречает затруднений. Синтаксис регулярных выражений до сих пор не полностью стандартизован. Существует POSIX-версия регулярных выражений, полностью описывающая стандарт синтаксиса для POSIX. Но версия Perl шире и более гибка, чем и объясняется ее широкая распространенность. Большинство библиотек по синтаксису и используемым метасимволам совместимо с Perl, поэтому имеет смысл начать разбираться с использованием регулярных выражений на примере именно этого языка. Три типа машин регулярных выражений На практике применяются три типа машин регулярных выражений. 1. DFA (Deterministic Finite-State Automaton – детерминированные конечные автоматы) машины работают линейно по времени, поскольку не нуждаются в откатах (и никогда не проверяют один символ дважды)
2. Что дает и что отнимает «черный нал» у бизнеса
3. Система творческих работ учащихся в 5–6-х классах
4. Все тесты по русскому языку для 5-6 классов
9. Развитие математических способностей у детей 5-6 лет
10. Формы и методы организации и проведения гимнастики в 5-6 классах
11. Особенности формирования коммуникативной функции речи у детей 5-6 лет с общим недоразвитием речи
12. Синтаксический анализ языка НОРМА. Разбор описания
15. Разбор рассказа В. М. Шукшина "Мастер"
16. Синтаксическая и фонетическая стилистика
18. Синтаксический распознаватель арифметического оператора условного перехода языка FORTRAN
19. Морфемный разбор (разбор слова по составу)
21. Синтаксическая синонимичность
25. Компьютерный морфологический разбор слов русского языка
26. Алгоритм нисходящего разбора. Нисходящие распознаватели
27. Из разборов лирики А.А. Фета: «Кот поет, глаза прищуря»
28. Клинический разбор: девочка полутора лет с водянистой диареей и потерей массы тела
30. Синтаксические особенности научных текстов Л.В. Щербы
31. Синтаксические структуры - англицизмы в языке масс-медиа
32. Мастерство критического разбора А.С. Пушкина (о жизни и сочинениях Озерова)
33. Обучение орфографии учеников 5 класса общеобразовательных учреждение
35. Что такое звезды
36. Что такое звёзды
37. Гранаты РГД-5
41. Трактовка образа Обломова в статье Н. А. Добролюбова "Что такое Обломовщина?"
42. Опера - всё, что нужно знать о неё, прежде чем её посетить
43. Великая Отечественная война. Что было и что должно было быть
44. Что я знаю о Великой Отечественной Войне
45. Старая пластинка: Что такое цифровой звук и реставрация звука с помощью цифровой обработки
46. Что является CDMA (Разделение Кодекса Многократный Доступ)?
47. Что такое мультимедийный компьютер?
49. Пример программирония на Бейсике (результаты сессии 25 студентов, сдавших 5 экзаменов)
50. Лабораторная работа №5 по "Основам теории систем" (Транспортные задачи линейного программирования)
51. Математическая кунсткамера /кое-что из истории геометрии/
52. Лекции по Методике математики в начальных классах (4-5 семестры)
53. Экологический кризис: что охранять и как использовать?
57. Методы изучения развитости личности ученика
58. Кто правит и что правит. Сила власти или власть силы
59. Виды современного копировального оборудования. Что и как выбирать
60. Что закрепляется в моей памяти и почему ?
61. Иллюзии восприятия, или всегда ли мы видим то, что видим
62. Психолого-педагогическая характеристика 5 класса
63. Особенности интеллекта учеников специализированных классов (гуманитарного и математического)
64. Усилитель мощности 1-5 каналов ТВ
65. Лабораторная работа номер 5 по информатике
68. Тест для 5-х классов по гандболу
69. Что такое свобода личности и в чем смысл жизни?
73. Бизнес-план. Что это такое?
74. Что и как изучает история?
75. Что такое «устойчивое развитие» для Украины?
76. Что такое "любовь к империи"
77. Государство без грозы, что конь без узды
78. Столкновение цивилизаций и что оно может означать для России
79. Македония в 5-3 вв. до н.э.
81. Введение в ХХ век. Что такое модернизация ?
82. Накопители на гибких магнитных дисках: что это такое и способ производства
84. ПБОЮЛ или ООО. Что выгоднее?
85. Что такое книжная иллюстрация
89. Искусство Греческой классики (Начало 5 — середина 4 в. до н.э.)
90. Ж.-Ф. Лиотар "Ответ на вопрос: что такое постмодерн?"
91. Кто и что сегодня читает и зачем им это нужно
92. Что в мире и человеке открыл мне роман М. А. Булгакова «Мастер и Маргарита»?
93. Согласны ли вы с А. С. Пушкиным в том, что “России определено было высшее назначение”?
94. Что было непонятно Базарову
95. Что такое любовь в представлении леди Макбет?
96. Надо ли быть компетентным, если пишешь книгу, или "не все то золото, что блестит"
97. Что несут Воланд и его спутники миру, зло или добро?
100. Философские взгляды в романе Н.Г. Чернышевского "Что делать?"