Реализовать конечный автомат по разбору строки (сделал, но есть ошибки) - C (СИ)

Узнай цену своей работы

Формулировка задачи:

Требовалось на С/С++ реализовать конечный автомат по разбору введенной строки. Я это сделал но есть проблемы с выводом.

Задание:

Входные данные:

Вводится строка, по виду представляющая собой команду из командной строки

Правила:

Вводится "команда" и еще дополнительно можно ввести "параметры команды", "ключи" и "параметры ключей". Последовательность: Команда, параметры команды, ключи, параметры ключей. Команда вводится обязательно, остальное по желанию. Команда состоит из любой последовательности латинских букв (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;
            }
//===========================================================================
        }    
    }    
}
У меня почему то не выполняются строки кода: 338, 354, 356, 383, 385, 405, 419, 426. Такое чувство что не выполняются условия. Это как раз тот самый вывод, которой показывает где команда, где ключи, где параметры. Если бы эти строки выполнялись, задача была бы решена. Вопрос: Почему не выполняются эти строки кода? Как заставить их выполняться? Буду очень благодарен тем, кто хоть чем то поможет.

Решение задачи: «Реализовать конечный автомат по разбору строки (сделал, но есть ошибки)»

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;
//===========================================================================
            }
        }   
    }   
}

Объяснение кода листинга программы

В коде присутствуют ошибки, которые могут быть исправлены следующим образом:

  1. В условии if (StringEndedFlag == 0) LetterReader++; нужно заменить LetterReader на BufferReader, так как мы увеличиваем счетчик чтения из буфера, а не счетчик чтения букв.
  2. В условии if (StringEndedFlag == 1) return 0; нужно добавить BufferWriter(); перед возвратом, чтобы записать оставшиеся символы в буфер перед его очисткой.
  3. В условии if (letter==34) Group = 2; нужно добавить BufferWriter(); перед возвратом, чтобы записать оставшиеся символы в буфер перед его очисткой.
  4. В условии if (letter==45) нужно заменить DashOccurrenceFlag = 1; на DashOccurrenceFlag = 0;, так как тире должно встречаться только один раз.
  5. В условии if (letter==0 && StringEndedFlag != 2) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать конец буфера.
  6. В условии if (letter==0 && StringEndedFlag == 2) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать конец строки.
  7. В условии if (letter==99 && DashOccurrenceFlag == 1) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать команду WeMet.
  8. В условии if (letter>=108 && letter<=110 && DashOccurrenceFlag == 1) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать ключи l, m, n, s.
  9. В условии if (letter==60 || letter==62 || letter==63 || letter==58) нужно заменить return 0; на BufferWriter(); и DashOccurrenceFlag = 0;, чтобы корректно обработать запрещенные символы < > : ?.

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

13   голосов , оценка 4 из 5
Похожие ответы