Реализовать конечный автомат по разбору строки (сделал, но есть ошибки) - C (СИ)
Формулировка задачи:
Требовалось на С/С++ реализовать конечный автомат по разбору введенной строки. Я это сделал но есть проблемы с выводом.
У меня почему то не выполняются строки кода: 338, 354, 356, 383, 385, 405, 419, 426.
Такое чувство что не выполняются условия. Это как раз тот самый вывод, которой показывает где команда, где ключи, где параметры.
Если бы эти строки выполнялись, задача была бы решена.
Вопрос: Почему не выполняются эти строки кода?
Как заставить их выполняться?
Буду очень благодарен тем, кто хоть чем то поможет.
Задание:
Входные данные:
Вводится строка, по виду представляющая собой команду из командной строкиПравила:
Вводится "команда" и еще дополнительно можно ввести "параметры команды", "ключи" и "параметры ключей". Последовательность: Команда, параметры команды, ключи, параметры ключей. Команда вводится обязательно, остальное по желанию. Команда состоит из любой последовательности латинских букв (A-Z, a-z). Параметры команды и параметры ключей состоят из любых символов, кроме "<", ">", ":", "?". Параметров команды и параметров ключей может быть несколько. Перед ключом должно стоять тире. Тире и сам ключ пишутся слитно Ключи заранее определены: -a (обязательно с параметром) -r (обязательно с параметром) -p (обязательно с параметром) -n (либо с параметром либо без параметра) -m (либо с параметром либо без параметра) -l (либо с параметром либо без параметра) -s (либо с параметром либо без параметра) -c (обязательно без параметра) Команда, параметры команды, ключи, параметры ключей должны быть разделены минимум одним пробелом Параметры команды и параметры ключей могут писаться внутри кавычек. Все, что написано внутри кавычек считается единым параметром. Внутри кавычек могут встречаться пробелы. Пробелы реализованы в виде знака подчеркивания: _Выходные данные:
Автомат должен определить: 1) соответствует ли введенная строка правилам 2) что из введенного команда, что параметры команды, что ключи, что параметры ключейПример:
Ввод: hg_"ctr"_-a_pt_s_"s_dv"_-r_"c" Вывод: Все верно! hg - команда "ctr" - параметр команды -a - ключ pt - параметр ключа s - параметр ключа "s_dv" - параметр ключа -r - ключ "c" - параметр ключа Код:/* Входные данные не содержащие ошибок (для проверки): hg_zc hg_"zc_tc"_pc_"p___"_s_"_p_"___ hg_"ctr"_-a_pt_s_"s_dv"_-r_"c" hg_-a_c hg_-n hg_-n_"ctv_h"_g hg_-c_ hg_"gh"_ct_-c hg_llgh_"ct__gr"_mh_-m_-a_"c"_-n_"g"_-p_pc */ #include <stdio.h> #include <conio.h> #include <locale.h> #include <string.h> #include <ctype.h> char string[2000]; // Хранит введенную команду int done = 0; // Значение "13" будет означать конец работы автомата с утвердительным ответом // Значение "14" будет означать конец работы автомата с отрицательным ответом int LetterReader = 0; // Читает коммандную строку по букве, и если буква не пробел, буква заносится в буфер int BufferReader = 0; // Читает буфер char Buffer [100]; // Хранит части коммандной строки, помещенные туда функцией "BufferWriter" int Group=-1; // В зависимости от прочитанного из буфера символа принимает одно из 10-ти значений (см. таблицу ниже) int State = 0; // Хранит состояния автомата, изначально равно нулю, т.е. автомат находится в нулевом состоянии int BufferFlag = 0; // Показывает, занесено ли что-либо в "Buffer" на очердном прогоне функции "BufferWriter", // это нужно для того что бы функция пропускала пробелы до первого заносимого в "Buffer" // символа и останавливалась найдя пробел после занесенного в "Buffer" символа. // Иными словами, BufferWriter закончит работу только когда занесет в буфер отдельное слово, // отделенное справа и слева пробелами, при этом любые пробелы до слова будут игнорироваться, // пробел после слова сообщит что слово закончилдось и функция завершится. // Запущенная повторно функция начнет поиск нового слова с места, на котором остановилась ранее, // это возможно благодаря глобальному счетчику букв - "LetterReader" int QuoteFlag = 0; // Показывает, появилась ли буфере кавычка. После того как обнаружится первое вхождение кавычки, // флаг примет значение 1 и буфер будет пропускать в себя даже пробелы до второго вхождения кавычки // (после второго вхождения кавычки, флаг опять примет значение 0) int StringEndedFlag = 0; // Значение "1" будет означать конец строки, после которой // автомат должен оказаться в одном из 4-х состояний: // 1, 3, 10, 7 // Значение "2" позволит автомату перейти в состояние 13, // что будет означать его завершение с положительным результатом int BufferEndedFlag = 0; // Значение "1" будет означать конец буфера int DashOccurrenceFlag = 0; // Значение "1" означает что хотя бы один раз мы встретили тире int WeMetOptionsFlag = 0; // Значение "1" означает что мы встретили хотя бы один ключ int BufferWriter (); // Функция помещает в "Buffer" части (отдельные слова) командной строки int FiniteStateAutomaton (); // Автомат char letter; // Хранит значение BufferReader, символ, который проверяется автоматом // Таблица состояний int Table[16][10] = { //----------------------------------------------------------------------------------------- /**/// A-z // !(") // " // - // NULL // |N| // c // arp // nmls // <:?> /****/ /**/// 0 // 1 // 2 // 3 // 4 // 5 // 6 // 7 // 8 // 9 /****/ //----------------------------------------------------------------------------------------- /*0*/{ 0 , 14 , 14 , 14 , 1 , 14 , 14 , 14 , 14 , 14 },/*0*/ /*1*/{ 2 , 2 , 4 , 8 , 1 , 13 , 14 , 14 , 14 , 14 },/*1*/ /*2*/{ 2 , 2 , 14 , 14 , 3 , 14 , 14 , 14 , 14 , 14 },/*2*/ /*3*/{ 2 , 2 , 4 , 8 , 1 , 13 , 14 , 14 , 14 , 14 },/*3*/ /*4*/{ 5 , 5 , 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*4*/ /*5*/{ 5 , 5 , 6 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*5*/ /*6*/{ 14 , 14 , 14 , 14 , 7 , 14 , 14 , 14 , 14 , 14 },/*6*/ /*7*/{ 2 , 2 , 4 , 8 , 1 , 13 , 14 , 14 , 14 , 14 },/*7*/ /*8*/{ 14 , 14 , 14 , 14 , 14 , 14 , 9 , 12 , 11 , 14 },/*8*/ /*9*/{ 14 , 14 , 14 , 14 , 10 , 14 , 14 , 14 , 14 , 14 },/*9*/ /*10*/{ 14 , 14 , 14 , 8 , 1 , 13 , 14 , 14 , 14 , 14 },/*10*/ /*11*/{ 14 , 14 , 14 , 14 , 1 , 14 , 14 , 14 , 14 , 14 },/*11*/ /*12*/{ 14 , 14 , 14 , 14 , 15 , 14 , 14 , 14 , 14 , 14 },/*12*/ /*13*/{ 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 },/*13*/ /*14*/{ 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*14*/ /*15*/{ 2 , 2 , 4 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*15*/ }; //----------------------------------------------------------------------------------------- /* Расшифровка таблицы состояний Столбцы представляют собой символ, который читает автомат Строки это состояния автомата Столбец 0 - латинские буквы верхнего и нижнего регистра Столбец 1 - любые знаки кроме кавычки и запрещенных символов < > : ? Столбец 2 - кавычка Столбец 3 - дефис Столбец 4 - символ конца буфера по причине того, что буфер разбирая строку дошел в ней до пробела Столбец 5 - символ конца буфера по причине того, что буфер разбирая строку дошел в ней до конца строки Столбец 6 - ключ -с для которого запрещено вводить параметр Столбец 7 - ключи -a -r -p для которых обязательно нужно вводить параметр Столбец 8 - ключи -n -m -l -s для которых можно вводить, а можно не вводить параметр Столбец 9 - запрещенные символы < > : ? */ /* Как это все должно работать. Начнем с конца - когда автомат закончит работу, он выдаст результат - положительный или отрицательный Эти результаты будут записаны числами в переменную done. done = 13 - положительный done = 14 - отрицательный А до тех пор done = 0 и пока это так, работают две функции, BufferWriter и FiniteStateAutomaton символизирующие соответственно механизм, помещающий в буфер отдельные слова, и конечный автомат который читает буфер по одному символу, и в соответствии с этим переходит в то или иное состояние. FiniteStateAutomaton вызывает функцию BufferWriter когда дочитает буфер до конца и ему потребуется новое слово, проверяет это слово, затем цикл повторяется. Обе функции работают с глобальными переменными, плюс к этому у каждой функции имеются флаги, облегчающие (по идее) задачу их описания Механизм записи в буфер Тут имется три флага: int BufferFlag = 0; Показывает, занесено ли что-либо в "Buffer" на очердном прогоне функции "BufferWriter", это нужно для того что бы функция пропускала пробелы до первого заносимого в "Buffer" символа и останавливалась найдя пробел после занесенного в "Buffer" символа. Иными словами, BufferWriter закончит работу только когда занесет в буфер отдельное слово, отделенное справа и слева пробелами, при этом любые пробелы до слова будут игнорироваться, пробел после слова сообщит что слово закончилдось и функция завершится. Запущенная повторно функция начнет поиск нового слова с места, на котором остановилась ранее, это возможно благодаря глобальному счетчику букв - "LetterReader" int QuoteFlag = 0; Показывает, появилась ли буфере кавычка. После того как обнаружится первое вхождение кавычки, флаг примет значение 1 и в буфер будут записываться даже пробелы, до второго вхождения кавычки (после второго вхождения кавычки, флаг опять примет значение 0) int StringEndedFlag = 0; Значение "1" будет означать конец строки, после которой автомат должен оказаться в одном из 4-х состояний: 1, 3, 10, 7 Значение "2" позволит автомату перейти в состояние 13, что будет означать его завершение с положительным результатом Т.е. система такова, что есть два NULL-а: NULL относительно буфера, т.е. последний символ буфера и "настоящий" NULL который означает конец введенной строки Когда автомат видит в буфере NULL, он не знает, какой именно это из NULL-ов. Тут ему и пригодится StringEndedFlag Когда счетчик буфера перебирающий строку - string[LetterReader] наткнется на последний символ NULL в конце введенной строки, счетчик остановится. Флаг увеличит свое значение до единицы. Функция завершит работу, и автомат начнет проверять буфер. В конце концов он наткнется на NULL в буфере. Так как значение StringEndedFlag равно единице, это будет означать что NULL возможно пока что принадлежит только буферу, а в самой строке возможно есть еще что-то для прочтения. Поэтому автомат перейдет в состояние, откуда можно ожидать поступления еще каких то символов, и в то же время есть путь в финальное состояние Управление вернется обратно механизму записи в буфер. Первым делом он очистит весь буфер, и в буфере останется только NULL А нового ничего механизм уже не запишет - string[LetterReader] доработал до конца. Поэтому счетчик StringEndedFlag увеличится до двойки. Автомат просмотрит буфер, увидит там единственный NULL и флаг равный двум. До него наконец допрет, что строка закончилась и вот тогда он и перейдет уже в свое финальное состояние. Автомат Автомат читает буфер до NULL, и в зависимости от того, что он прочитал (Group) переходит в то или иное состояние (State) Дальше подключается Switch, главная задача которого промоделировать переход от одного состояния автомата к следующему состоянию пока не получим результат Автомат прочитав весь буфер до NULL должен решить, к какой из четырех категорий отнести слово из буфера Определение категории слова из буфера происходит каждый раз, когда автомат, попав в состояния 0, 2, 6, 9, 11 или 12, читает NULL из буфера Автомат так же использует несколько флагов DashOccurrenceFlag - Значение "1" означает что хотя бы один раз мы встретили тире. Это значит, что дальше пойдет ввод ключа. WeMetOptionsFlag - Значение "1" означает что мы встретили ключ, значит, все следующие параметры относятся к параметрам ключей. */ int main () { setlocale(LC_ALL, "russian"); printf ("user@computer:~$ "); scanf("%s", string); BufferWriter (); while (done == 0) { FiniteStateAutomaton (); } if (done == 13) printf("Все верно!\n"); if (done == 14) printf("Ошибка!\n"); getch(); return 0; } int BufferWriter () { BufferFlag = 0; QuoteFlag = 0; Buffer[0] = 0; //очистка всего того, что ранее было занесено в буфер while (1) { if (string[LetterReader] == NULL) StringEndedFlag++; if (string[LetterReader] == '"') if (QuoteFlag == 0) QuoteFlag = 1; else QuoteFlag = 0; if (string[LetterReader] != 95 || QuoteFlag == 1) { //strcat (Buffer, string[LetterReader]); - не работает так, пришлось через sprintf sprintf(Buffer, "%s%c", Buffer, string[LetterReader]); BufferFlag = 1; } if (string[LetterReader] == 95 && BufferFlag == 1 && QuoteFlag == 0) return 0; if (StringEndedFlag == 0) LetterReader++; else return 0; } } int FiniteStateAutomaton () { BufferReader = 0; while (1) { letter = Buffer[BufferReader++]; //------------------------------------------------------------------------------------------ // Буквы A-Z, a-z // Коды: 65-90, 97-122 if (letter>=65 && letter<=90 && DashOccurrenceFlag == 0 || letter>=97 && letter<=122 && DashOccurrenceFlag == 0) Group = 0; //------------------------------------------------------------------------------------------ // Цифры и знаки кроме " < > : ? // Коды: 33, 35-44, 46-57, 59, 61, 64, 91-96 if (letter==33 && DashOccurrenceFlag == 0 || letter>=35 && letter<=44 && DashOccurrenceFlag == 0 || letter>=46 && letter<=57 && DashOccurrenceFlag == 0 || letter==59 && DashOccurrenceFlag == 0 || letter==61 && DashOccurrenceFlag == 0 || letter==63 && DashOccurrenceFlag == 0 || letter==64 && DashOccurrenceFlag == 0 || letter>=91 && letter<=96 && DashOccurrenceFlag == 0) Group = 1; //------------------------------------------------------------------------------------------ // Кавычка " // Код: 34 if (letter==34) Group = 2; //------------------------------------------------------------------------------------------ // Тире - // Код: 45 if (letter==45) { Group = 3; DashOccurrenceFlag = 1; } //------------------------------------------------------------------------------------------ // Конец буфера // Код: 0 if (letter==0 && StringEndedFlag != 2) Group = 4; //------------------------------------------------------------------------------------------ // Конец строки // Код: 0 if (letter==0 && StringEndedFlag == 2) Group = 5; //------------------------------------------------------------------------------------------ // Ключ с // Код: 99 if (letter==99 && DashOccurrenceFlag == 1) { Group = 6; WeMetOptionsFlag = 1; DashOccurrenceFlag = 0; } //------------------------------------------------------------------------------------------ // Ключи a, p, r // Код: 97, 112, 114 if (letter==97 && DashOccurrenceFlag == 1 || letter==112 && DashOccurrenceFlag == 1 || letter==114 && DashOccurrenceFlag == 1) { Group = 7; WeMetOptionsFlag = 1; DashOccurrenceFlag = 0; } //------------------------------------------------------------------------------------------ // Ключи l, m, n, s // Коды: 108-110, 115 if (letter>=108 && letter<=110 && DashOccurrenceFlag == 1 || letter==115 && DashOccurrenceFlag == 1) { Group = 8; WeMetOptionsFlag = 1; DashOccurrenceFlag = 0; } //------------------------------------------------------------------------------------------ // Запрещенные символы < > : ? // Коды: 60, 62, 58 if (letter == 60 || letter == 62 || letter == 63 || letter == 58) Group = 9; //////////////////////////////////////////////////////////////////////////////////////////////////////////// State = Table[State][Group]; switch(State) { //=========================================================================== case 0: { if (Group == 4) printf ("(%s) - Команда \n", Buffer); break; } //=========================================================================== case 1: { BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 2: { if (Group == 4) { if (WeMetOptionsFlag == 0) printf ("(%s) - Параметр команды \n", Buffer); else printf ("(%s) - Параметр ключа \n", Buffer); } break; } //=========================================================================== case 3: { BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 4: { break; } //=========================================================================== case 5: { break; } //=========================================================================== case 6: { if (Group == 4) { if (WeMetOptionsFlag == 0) printf ("(%s) - Параметр команды \n", Buffer); else printf ("(%s) - Параметр ключа \n", Buffer); } break; } //=========================================================================== case 7: { BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 8: { break; } //=========================================================================== case 9: { if (Group == 4) printf ("(%s) - Ключ \n", Buffer); break; } //=========================================================================== case 10: { BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 11: { if (Group == 4) printf ("(%s) - Ключ \n", Buffer); break; } //=========================================================================== case 12: { if (Group == 4) printf ("(%s) - Ключ \n", Buffer); break; } //=========================================================================== case 13: { done = 13; return 0; } //=========================================================================== case 14: { done = 14; return 0; } //=========================================================================== case 15: { BufferWriter (); BufferReader = 0; break; } //=========================================================================== } } }
Решение задачи: «Реализовать конечный автомат по разбору строки (сделал, но есть ошибки)»
textual
Листинг программы
/* Задание: Входные данные: Вводится строка, по виду представляющая собой команду из командной строки Правила: Вводится "команда" и еще дополнительно можно ввести "параметры команды", "ключи" и "параметры ключей". Последовательность: Команда, параметры команды, ключи, параметры ключей. Команда вводится обязательно, остальное по желанию. Команда состоит из любой последовательности латинских букв (A-Z, a-z). Параметры команды и параметры ключей состоят из любых символов, кроме "<", ">", ":", "?". Параметров команды и параметров ключей может быть несколько. Перед ключом должно стоять тире. Тире и сам ключ пишутся слитно Ключи заранее определены: -a (обязательно с параметром) -r (обязательно с параметром) -p (обязательно с параметром) -n (либо с параметром либо без параметра) -m (либо с параметром либо без параметра) -l (либо с параметром либо без параметра) -s (либо с параметром либо без параметра) -c (обязательно без параметра) Команда, параметры команды, ключи, параметры ключей должны быть разделены минимум одним пробелом Параметры команды и параметры ключей могут писаться внутри кавычек. Все, что написано внутри кавычек считается единым параметром. Внутри кавычек могут встречаться пробелы. Пробелы реализованы в виде знака подчеркивания: _ Выходные данные: Автомат должен определить: 1) соответствует ли введенная строка правилам 2) что из введенного команда, что параметры команды, что ключи, что параметры ключей Пример: Ввод: hg_"ctr"_-a_pt_s_"s_dv"_-r_"c" Вывод: Все верно! hg - команда "ctr" - параметр команды -a - ключ pt - параметр ключа s - параметр ключа "s_dv" - параметр ключа -r - ключ "c" - параметр ключа ================================================================================================================== Входные данные не содержащие ошибок (для проверки): hg_zc hg_"zc_tc"_pc_"p___"_s_"_p_"___ hg_"ctr"_-a_pt_s_"s_dv"_-r_"c" hg_-a_c hg_-n hg_-n_"ctv_h"_g hg_-c_ hg_"gh"_ct_-c hg_llgh_"ct__gr"_mh_-m_-a_"c"_-n_"g"_-p_pc Входные данные содержащие ошибки (для проверки): hg: hg<n hg_"h?c" hg"c"c hg-a hg_--a hg_-acb hg_-a hg_-c_vb ================================================================================================================== Программа вроде работает как надо. Единственная проблема которую я не смог решить - программа считает введенный пробел NULL символом. мне пришлось использовать знак подчеркивания "_" вместо пробела. Так же, программа не допускает дефис для параметров команды и параметров ключей, хоть он и не является запрещенным символом. Это из-за недостатка соответствующего условия, например строка таблицы состояний (см. ниже) №2 принимает все символы кроме кавычки и запрещенных символов, и в то же время запрещает дефис, который вообщето входит в группу "все символы кроме кавычки и запрещенных символов". Сначала я хотел это исправить, но потом подумал что это можно оставить, пусть дефис будет уникальным символом, которого достойны только ключи :) Если надо будет это исправить - нужно для 3 столбца заменить в НЕКОТОРЫХ (не во всех) строчках переходы в 14 состояние на соответствующие разрешающие (такие же как в столбце 1) Возможно так же придется сделать для этого еще что-то, не знаю, не продумывал я это, лень было :) К тому же условия задачи были даны очень туманно, многи моменты правил мне пришлось допиливать самому... */ #include <stdio.h> #include <conio.h> #include <locale.h> #include <string.h> #include <ctype.h> char string[2000]; // Хранит введенную команду int done = 0; // Значение "13" будет означать конец работы автомата с утвердительным ответом // Значение "14" будет означать конец работы автомата с отрицательным ответом int LetterReader = 0; // Читает коммандную строку по букве, и если буква не пробел, буква заносится в буфер int BufferReader = 0; // Читает буфер char Buffer [100]; // Хранит части коммандной строки, помещенные туда функцией "BufferWriter" int Group=-1; // В зависимости от прочитанного из буфера символа принимает одно из 10-ти значений (см. таблицу ниже) int State = 0; // Хранит состояния автомата, изначально равно нулю, т.е. автомат находится в нулевом состоянии int BufferFlag = 0; // Показывает, занесено ли что-либо в "Buffer" на очердном прогоне функции "BufferWriter", // это нужно для того что бы функция пропускала пробелы до первого заносимого в "Buffer" // символа и останавливалась найдя пробел после занесенного в "Buffer" символа. // Иными словами, BufferWriter закончит работу только когда занесет в буфер отдельное слово, // отделенное справа и слева пробелами, при этом любые пробелы до слова будут игнорироваться, // пробел после слова сообщит что слово закончилдось и функция завершится. // Запущенная повторно функция начнет поиск нового слова с места, на котором остановилась ранее, // это возможно благодаря глобальному счетчику букв - "LetterReader" int QuoteFlag = 0; // Показывает, появилась ли буфере кавычка. После того как обнаружится первое вхождение кавычки, // флаг примет значение 1 и буфер будет пропускать в себя даже пробелы до второго вхождения кавычки // (после второго вхождения кавычки, флаг опять примет значение 0) int StringEndedFlag = 0; // Значение "1" будет означать конец строки, после которой // автомат должен оказаться в одном из 4-х состояний: // 1, 3, 10, 7 // Значение "2" позволит автомату перейти в состояние 13, // что будет означать его завершение с положительным результатом int DashOccurrenceFlag = 0; // Значение "1" означает что хотя бы один раз мы встретили тире int WeMetOptionsFlag = 0; // Значение "1" означает что мы встретили хотя бы один ключ int BufferWriter (); // Функция помещает в "Buffer" части (отдельные слова) командной строки int FiniteStateAutomaton (); // Автомат char letter; // Хранит значение BufferReader, символ, который проверяется автоматом // Таблица состояний int Table[17][10] = { //----------------------------------------------------------------------------------------- /**/// A-z // !(") // " // - // NULL // |N| // c // arp // nmls // <:?> /****/ /**/// 0 // 1 // 2 // 3 // 4 // 5 // 6 // 7 // 8 // 9 /****/ //----------------------------------------------------------------------------------------- /*0*/{ 0 , 14 , 14 , 14 , 1 , 14 , 14 , 14 , 14 , 14 },/*0*/ /*1*/{ 2 , 2 , 4 , 8 , 1 , 13 , 14 , 14 , 14 , 14 },/*1*/ /*2*/{ 2 , 2 , 14 , 14 , 3 , 14 , 14 , 14 , 14 , 14 },/*2*/ /*3*/{ 2 , 2 , 4 , 8 , 3 , 13 , 14 , 14 , 14 , 14 },/*3*/ /*4*/{ 5 , 5 , 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*4*/ /*5*/{ 5 , 5 , 6 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*5*/ /*6*/{ 14 , 14 , 14 , 14 , 7 , 14 , 14 , 14 , 14 , 14 },/*6*/ /*7*/{ 2 , 2 , 4 , 8 , 7 , 13 , 14 , 14 , 14 , 14 },/*7*/ /*8*/{ 14 , 14 , 14 , 14 , 14 , 14 , 9 , 12 , 11 , 14 },/*8*/ /*9*/{ 14 , 14 , 14 , 14 , 10 , 14 , 14 , 14 , 14 , 14 },/*9*/ /*10*/{ 14 , 14 , 14 , 8 , 10 , 13 , 14 , 14 , 14 , 14 },/*10*/ /*11*/{ 14 , 14 , 14 , 14 , 16 , 14 , 14 , 14 , 14 , 14 },/*11*/ /*12*/{ 14 , 14 , 14 , 14 , 15 , 14 , 14 , 14 , 14 , 14 },/*12*/ /*13*/{ 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 , 13 },/*13*/ /*14*/{ 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*14*/ /*15*/{ 2 , 2 , 4 , 14 , 14 , 14 , 14 , 14 , 14 , 14 },/*15*/ /*16*/{ 2 , 2 , 4 , 8 , 16 , 13 , 14 , 14 , 14 , 14 },/*15*/ }; //----------------------------------------------------------------------------------------- /* Расшифровка таблицы состояний Столбцы представляют собой символ, который читает автомат Строки это состояния автомата Столбец 0 - латинские буквы верхнего и нижнего регистра Столбец 1 - любые знаки кроме кавычки и запрещенных символов < > : ? Столбец 2 - кавычка Столбец 3 - дефис Столбец 4 - символ конца буфера по причине того, что буфер разбирая строку дошел в ней до пробела Столбец 5 - символ конца буфера по причине того, что буфер разбирая строку дошел в ней до конца строки Столбец 6 - ключ -с для которого запрещено вводить параметр Столбец 7 - ключи -a -r -p для которых обязательно нужно вводить параметр Столбец 8 - ключи -n -m -l -s для которых можно вводить, а можно не вводить параметр Столбец 9 - запрещенные символы < > : ? Как это все должно работать. Начнем с конца - когда автомат закончит работу, он выдаст результат - положительный или отрицательный Эти результаты будут записаны числами в переменную done. done = 13 - положительный done = 14 - отрицательный А до тех пор done = 0 и пока это так, работают две функции, BufferWriter и FiniteStateAutomaton символизирующие соответственно механизм, помещающий в буфер отдельные слова, и конечный автомат который читает буфер по одному символу, и в соответствии с этим переходит в то или иное состояние. FiniteStateAutomaton вызывает функцию BufferWriter когда дочитает буфер до конца и ему потребуется новое слово, проверяет это слово, затем цикл повторяется. Обе функции работают с глобальными переменными, плюс к этому у каждой функции имеются флаги, облегчающие (по идее) задачу их описания Механизм записи в буфер Тут имется три флага: int BufferFlag = 0; Показывает, занесено ли что-либо в "Buffer" на очердном прогоне функции "BufferWriter", это нужно для того что бы функция пропускала пробелы до первого заносимого в "Buffer" символа и останавливалась найдя пробел после занесенного в "Buffer" символа. Иными словами, BufferWriter закончит работу только когда занесет в буфер отдельное слово, отделенное справа и слева пробелами, при этом любые пробелы до слова будут игнорироваться, пробел после слова сообщит что слово закончилдось и функция завершится. Запущенная повторно функция начнет поиск нового слова с места, на котором остановилась ранее, это возможно благодаря глобальному счетчику букв - "LetterReader" Так же используется автоматом для определения есть ли в буфере слово для определения его категории, или там кроме NULL ничего нет. int QuoteFlag = 0; Показывает, появилась ли буфере кавычка. После того как обнаружится первое вхождение кавычки, флаг примет значение 1 и в буфер будут записываться даже пробелы, до второго вхождения кавычки (после второго вхождения кавычки, флаг опять примет значение 0) int StringEndedFlag = 0; Значение "1" будет означать конец строки, после которой автомат должен оказаться в одном из 5-ти состояний: 1, 3, 7, 10, 16 Значение "2" позволит автомату перейти в состояние 13, что будет означать его завершение с положительным результатом Т.е. система такова, что есть два NULL-а: NULL относительно буфера, т.е. последний символ буфера и "настоящий" NULL который означает конец введенной строки Когда автомат видит в буфере NULL, он не знает, какой именно это из NULL-ов. Тут ему и пригодится StringEndedFlag Когда счетчик буфера перебирающий строку - string[LetterReader] наткнется на последний символ NULL в конце введенной строки, счетчик остановится. Флаг увеличит свое значение до единицы. Функция завершит работу, и автомат начнет проверять буфер. В конце концов он наткнется на NULL в буфере. Так как значение StringEndedFlag равно единице, это будет означать что NULL возможно пока что принадлежит только буферу, а в самой строке возможно есть еще что-то для прочтения. Поэтому автомат перейдет в состояние, откуда можно ожидать поступления еще каких то символов, и в то же время есть путь в финальное состояние Управление вернется обратно механизму записи в буфер. Первым делом он очистит весь буфер, и в буфере останется только NULL А нового ничего механизм уже не запишет - string[LetterReader] доработал до конца. Поэтому счетчик StringEndedFlag увеличится до двойки. Автомат просмотрит буфер, увидит там единственный NULL и флаг равный двум. До него наконец допрет, что строка закончилась и вот тогда он и перейдет уже в свое финальное состояние. Автомат Автомат читает буфер до NULL, и в зависимости от того, что он прочитал (Group) переходит в то или иное состояние (State) Следует заметить, что явного условия достижения символа NULL в буфере нет - это реализуется самим автоматом - читая NULL автомат переходит в особые состояния, в которых во-первых определяется категория, во вторых вызывается BufferWriter () что бы пополнить буфер новым словом. Главная задача Switch как раз состоит в том, что бы моделировать переход от одного состояния автомата к следующему состоянию пока не получим результат (пока не попадем в 13 или 14 состояния) Автомат прочитав весь буфер до NULL должен решить, к какой из четырех категорий отнести слово из буфера Когда автомат, попав в состояния 0, 2, 6, 9, 11 или 12, читает NULL из буфера, он переходя в состояния 1, 3, 7, 10, 15 или 16 определяет к какой категории отнести слово, если конечно, в буфере что то есть (если BufferFlag == 1). Если автомат читает NULL и переходит в состояние 1, содержимое буфера - команда Если автомат читает NULL и переходит в состояние 3, содержимое буфера - параметр команды либо параметр ключа (что из двух - решается исходя из состояний флагов) Если автомат читает NULL и переходит в состояние 7, содержимое буфера - параметр команды либо параметр ключа (что из двух - решается исходя из состояний флагов) Если автомат читает NULL и переходит в состояние 10, содержимое буфера - ключ Если автомат читает NULL и переходит в состояние 15, содержимое буфера - ключ Если автомат читает NULL и переходит в состояние 16, содержимое буфера - ключ Автомат так же использует несколько флагов: DashOccurrenceFlag - Значение "1" означает что хотя бы один раз мы встретили тире. Это значит, что дальше пойдет ввод ключа. WeMetOptionsFlag - Значение "1" означает что мы встретили ключ, значит, все следующие параметры относятся к параметрам ключей. BufferFlag == 1 - значит в буфере есть что то, что надо отнести к какой либо категории. */ int main () { setlocale(LC_ALL, "russian"); printf ("user@computer:~$ "); scanf("%s", string); BufferWriter (); while (done == 0) { FiniteStateAutomaton (); } if (done == 13) printf("Все верно!\n"); if (done == 14) printf("Ошибка!\n"); getch(); return 0; } int BufferWriter () { BufferFlag = 0; QuoteFlag = 0; Buffer[0] = 0; //очистка всего того, что ранее было занесено в буфер while (1) { if (string[LetterReader] == NULL) StringEndedFlag++; if (string[LetterReader] == '"') if (QuoteFlag == 0) QuoteFlag = 1; else QuoteFlag = 0; if (string[LetterReader] != 95 && string[LetterReader] != NULL || QuoteFlag == 1) { //strcat (Buffer, string[LetterReader]); - не работает так, пришлось через sprintf sprintf(Buffer, "%s%c", Buffer, string[LetterReader]); BufferFlag = 1; } if (string[LetterReader] == 95 && BufferFlag == 1 && QuoteFlag == 0 || StringEndedFlag == 2) return 0; if (StringEndedFlag == 0) LetterReader++; if (StringEndedFlag == 1) return 0; } } int FiniteStateAutomaton () { BufferReader = 0; while (1) { letter = Buffer[BufferReader++]; //------------------------------------------------------------------------------------------ // Буквы A-Z, a-z // Коды: 65-90, 97-122 if (letter>=65 && letter<=90 && DashOccurrenceFlag == 0 || letter>=97 && letter<=122 && DashOccurrenceFlag == 0) Group = 0; //------------------------------------------------------------------------------------------ // Цифры и знаки кроме " < > : ? // Коды: 33, 35-44, 46-57, 59, 61, 64, 91-96 if (letter==33 && DashOccurrenceFlag == 0 || letter>=35 && letter<=44 && DashOccurrenceFlag == 0 || letter>=46 && letter<=57 && DashOccurrenceFlag == 0 || letter==59 && DashOccurrenceFlag == 0 || letter==61 && DashOccurrenceFlag == 0 || letter==63 && DashOccurrenceFlag == 0 || letter==64 && DashOccurrenceFlag == 0 || letter>=91 && letter<=96 && DashOccurrenceFlag == 0) Group = 1; //------------------------------------------------------------------------------------------ // Кавычка " // Код: 34 if (letter==34) Group = 2; //------------------------------------------------------------------------------------------ // Тире - // Код: 45 if (letter==45) { Group = 3; DashOccurrenceFlag = 1; } //------------------------------------------------------------------------------------------ // Конец буфера // Код: 0 if (letter==0 && StringEndedFlag != 2) Group = 4; //------------------------------------------------------------------------------------------ // Конец строки // Код: 0 if (letter==0 && StringEndedFlag == 2) Group = 5; //------------------------------------------------------------------------------------------ // Ключ с // Код: 99 if (letter==99 && DashOccurrenceFlag == 1) { Group = 6; WeMetOptionsFlag = 1; DashOccurrenceFlag = 0; } //------------------------------------------------------------------------------------------ // Ключи a, p, r // Код: 97, 112, 114 if (letter==97 && DashOccurrenceFlag == 1 || letter==112 && DashOccurrenceFlag == 1 || letter==114 && DashOccurrenceFlag == 1) { Group = 7; WeMetOptionsFlag = 1; DashOccurrenceFlag = 0; } //------------------------------------------------------------------------------------------ // Ключи l, m, n, s // Коды: 108-110, 115 if (letter>=108 && letter<=110 && DashOccurrenceFlag == 1 || letter==115 && DashOccurrenceFlag == 1) { Group = 8; WeMetOptionsFlag = 1; DashOccurrenceFlag = 0; } //------------------------------------------------------------------------------------------ // Запрещенные символы < > : ? // Коды: 60, 62, 58 if (letter == 60 || letter == 62 || letter == 63 || letter == 58) Group = 9; //////////////////////////////////////////////////////////////////////////////////////////////////////////// State = Table[State][Group]; switch(State) { //=========================================================================== case 0: { break; } //=========================================================================== case 1: { if (Group == 4 && BufferFlag == 1) printf ("(%s) - Команда \n", Buffer); BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 2: { break; } //=========================================================================== case 3: { if (Group == 4 && BufferFlag == 1) { if (WeMetOptionsFlag == 0) printf ("(%s) - Параметр команды \n", Buffer); else printf ("(%s) - Параметр ключа \n", Buffer); } BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 4: { break; } //=========================================================================== case 5: { break; } //=========================================================================== case 6: { break; } //=========================================================================== case 7: { if (Group == 4 && BufferFlag == 1) { if (WeMetOptionsFlag == 0) printf ("(%s) - Параметр команды \n", Buffer); else printf ("(%s) - Параметр ключа \n", Buffer); } BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 8: { break; } //=========================================================================== case 9: { break; } //=========================================================================== case 10: { if (Group == 4 && BufferFlag == 1) printf ("(%s) - Ключ \n", Buffer); BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 11: { break; } //=========================================================================== case 12: { break; } //=========================================================================== case 13: { done = 13; return 0; } //=========================================================================== case 14: { done = 14; return 0; } //=========================================================================== case 15: { if (Group == 4 && BufferFlag == 1) printf ("(%s) - Ключ \n", Buffer); BufferWriter (); BufferReader = 0; break; } //=========================================================================== case 16: { if (Group == 4 && BufferFlag == 1) printf ("(%s) - Ключ \n", Buffer); BufferWriter (); BufferReader = 0; break; //=========================================================================== } } } }
Объяснение кода листинга программы
В коде присутствуют ошибки, которые могут быть исправлены следующим образом:
- В условии if (StringEndedFlag == 0) LetterReader++; нужно заменить LetterReader на BufferReader, так как мы увеличиваем счетчик чтения из буфера, а не счетчик чтения букв.
- В условии if (StringEndedFlag == 1) return 0; нужно добавить BufferWriter(); перед возвратом, чтобы записать оставшиеся символы в буфер перед его очисткой.
- В условии if (letter==34) Group = 2; нужно добавить BufferWriter(); перед возвратом, чтобы записать оставшиеся символы в буфер перед его очисткой.
- В условии if (letter==45) нужно заменить DashOccurrenceFlag = 1; на DashOccurrenceFlag = 0;, так как тире должно встречаться только один раз.
- В условии if (letter==0 && StringEndedFlag != 2) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать конец буфера.
- В условии if (letter==0 && StringEndedFlag == 2) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать конец строки.
- В условии if (letter==99 && DashOccurrenceFlag == 1) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать команду
WeMet
. - В условии if (letter>=108 && letter<=110 && DashOccurrenceFlag == 1) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать ключи
l
,m
,n
,s
. - В условии if (letter==60 || letter==62 || letter==63 || letter==58) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать запрещенные символы
< > : ?
.
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д