Конечный автомат на чистом С - C (СИ)

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

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

Здравствуйте, уважаемые! Недавно предложили мне для выполнения задание по фильтрации текста, а именно удалить из текста комментарии. Когда же я с ним справился обычным способом(при помощи if, else) предложили справиться иным. Дали прочитать данную статью https://habrahabr.ru/post/241941/ и предложили методом, описываемым в оной статье избавиться от комментариев из файла. На данный момент эта программа есть, и она работает:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
 
//ВАЖНО!!! Программа может работать только с комментариями из символов стандартной ASCII, так как буква я имеет код -1, 
//также как и EOF, что приводит к преждевременному выходу из цикла!!!
 
/*
 
enum states - это состояния определяющие вывод
no_Comment - означает, что на данный момент из файла считывается не строка
pre_Comment - означает, что был считан символ /, а значит может начать комментарий
comment_TYPE1 - означает, что после символа /, был считан еще один символ /, и начался комментарий типа //
comment_TYPE2 - означает, что после символа /, был считан еще один символ *, и начался комментарий типа /*
fin_Comment - означает, что в момент состояния comment_TYPE2, был считан символ *, и следующий символ завершит комментарий
absolutly_Fin_Comment - означает, что в момент состояния fin_Comment, был считан символ /, и комментарий типа /* завершен
string1 - означает, что в состоянии no_Comment, был считан символ ', а значит началась строка, в которой не может быть комментариев
string2 - означает, что в состоянии no_Comment, был считан символ ", а значит началась строка, в которой не может быть комментариев
 
*/
enum states { no_Comment = 0, pre_Comment, comment_TYPE1, comment_TYPE2, fin_Comment, absolutly_Fin_Comment, string1, string2 };
 
/*
 
enum symbols -  при помощи функции enum symbols translate(char symbol); определяет были ли введены символы, которые могут поменять состояние определяющее вывод
 
*/
 
enum symbols { SLASH = 0, STAR, SINGLE_QUOTES, DOUBLE_QUOTES, INDENT, ANOTHER };
 
enum states table_For_States[8][6] = {
    [no_Comment][SLASH] = pre_Comment,
    [no_Comment][STAR] = no_Comment,
    [no_Comment][SINGLE_QUOTES] = string1,
    [no_Comment][DOUBLE_QUOTES] = string2,
    [no_Comment][INDENT] = no_Comment,
    [no_Comment][ANOTHER] = no_Comment,
    //--------------------------------------------
    [pre_Comment][SLASH] = comment_TYPE1,
    [pre_Comment][STAR] = comment_TYPE2,
    [pre_Comment][SINGLE_QUOTES] = string1,
    [pre_Comment][DOUBLE_QUOTES] = string2,
    [pre_Comment][INDENT] = no_Comment,
    [pre_Comment][ANOTHER] = no_Comment,
    //--------------------------------------------
    [comment_TYPE1][SLASH] = comment_TYPE1,
    [comment_TYPE1][STAR] = comment_TYPE1,
    [comment_TYPE1][SINGLE_QUOTES] = comment_TYPE1,
    [comment_TYPE1][DOUBLE_QUOTES] = comment_TYPE1,
    [comment_TYPE1][INDENT] = no_Comment,
    [comment_TYPE1][ANOTHER] = comment_TYPE1,
    //--------------------------------------------
    [comment_TYPE2][SLASH] = comment_TYPE2,
    [comment_TYPE2][STAR] = fin_Comment,
    [comment_TYPE2][SINGLE_QUOTES] = comment_TYPE2,
    [comment_TYPE2][DOUBLE_QUOTES] = comment_TYPE2,
    [comment_TYPE2][INDENT] = comment_TYPE2,
    [comment_TYPE2][ANOTHER] = comment_TYPE2,
    //--------------------------------------------
    [fin_Comment][SLASH] = absolutly_Fin_Comment,
    [fin_Comment][STAR] = fin_Comment,
    [fin_Comment][SINGLE_QUOTES] = comment_TYPE2,
    [fin_Comment][DOUBLE_QUOTES] = comment_TYPE2,
    [fin_Comment][INDENT] = comment_TYPE2,
    [fin_Comment][ANOTHER] = comment_TYPE2,
    //--------------------------------------------
    [absolutly_Fin_Comment][SLASH] = pre_Comment,
    [absolutly_Fin_Comment][STAR] = no_Comment,
    [absolutly_Fin_Comment][SINGLE_QUOTES] = string1,
    [absolutly_Fin_Comment][DOUBLE_QUOTES] = string2,
    [absolutly_Fin_Comment][INDENT] = no_Comment,
    [absolutly_Fin_Comment][ANOTHER] = no_Comment,
    //--------------------------------------------
    [string1][SLASH] = string1,
    [string1][STAR] = string1,
    [string1][SINGLE_QUOTES] = no_Comment,
    [string1][DOUBLE_QUOTES] = string1,
    [string1][INDENT] = string1,
    [string1][ANOTHER] = string1,
    //--------------------------------------------
    [string2][SLASH] = string2,
    [string2][STAR] = string2,
    [string2][SINGLE_QUOTES] = string2,
    [string2][DOUBLE_QUOTES] = no_Comment,
    [string2][INDENT] = string2,
    [string2][ANOTHER] = string2
};
 
/*
 
enum is_Output - определяет, находится ли функция в состоянии, когда можно записывать данные в файл
FALSE - означает, что идет комментарий, и данный записывать не нужно
TRUE - означает, что комментарий отсутствует, и данные можно записывать в файл
ERROR - определяет ситуации, которых не может по логике программы, необходим на этапе отладки логики автомата

*/
 
enum is_Output {FALSE, TRUE, ERROR};
 
/*
 
enum is_Output table_For_Output - таблица, которая учитывая предыдущее состояние (которое записывается в первых []) и нынешнее состояние (которое записывается во вторых [])
определяет, можно ли сейчас записывать данные в файл
 
*/
 
enum is_Output table_For_Output[8][8] = {
    [no_Comment][no_Comment] = TRUE,
    [no_Comment][pre_Comment] = TRUE,
    [no_Comment][comment_TYPE1] = ERROR,
    [no_Comment][comment_TYPE2] = ERROR,
    [no_Comment][fin_Comment] = ERROR,
    [no_Comment][absolutly_Fin_Comment] = ERROR,
    [no_Comment][string1] = TRUE,
    [no_Comment][string2] = TRUE,
    //--------------------------------------------
    [pre_Comment][no_Comment] = TRUE,
    [pre_Comment][pre_Comment] = TRUE,
    [pre_Comment][comment_TYPE1] = FALSE,
    [pre_Comment][comment_TYPE2] = FALSE,
    [pre_Comment][fin_Comment] = ERROR,
    [pre_Comment][absolutly_Fin_Comment] = ERROR,
    [pre_Comment][string1] = TRUE,
    [pre_Comment][string2] = TRUE,
    //--------------------------------------------
    [comment_TYPE1][no_Comment] = FALSE,
    [comment_TYPE1][pre_Comment] = ERROR,
    [comment_TYPE1][comment_TYPE1] = FALSE,
    [comment_TYPE1][comment_TYPE2] = ERROR,
    [comment_TYPE1][fin_Comment] = ERROR,
    [comment_TYPE1][absolutly_Fin_Comment] = ERROR,
    [comment_TYPE1][string1] = ERROR,
    [comment_TYPE1][string2] = ERROR,
    //--------------------------------------------
    [comment_TYPE2][no_Comment] = FALSE,
    [comment_TYPE2][pre_Comment] = FALSE,
    [comment_TYPE2][comment_TYPE1] = ERROR,
    [comment_TYPE2][comment_TYPE2] = FALSE,
    [comment_TYPE2][fin_Comment] = FALSE,
    [comment_TYPE2][absolutly_Fin_Comment] = ERROR,
    [comment_TYPE2][string1] = ERROR,
    [comment_TYPE2][string2] = ERROR,
    //--------------------------------------------
    [fin_Comment][no_Comment] = FALSE,
    [fin_Comment][pre_Comment] = ERROR,
    [fin_Comment][comment_TYPE1] = ERROR,
    [fin_Comment][comment_TYPE2] = FALSE,
    [fin_Comment][fin_Comment] = FALSE,
    [fin_Comment][absolutly_Fin_Comment] = FALSE,
    [fin_Comment][string1] = ERROR,
    [fin_Comment][string2] = ERROR,
    //--------------------------------------------
    [absolutly_Fin_Comment][no_Comment] = FALSE,
    [absolutly_Fin_Comment][pre_Comment] = FALSE,
    [absolutly_Fin_Comment][comment_TYPE1] = ERROR,
    [absolutly_Fin_Comment][comment_TYPE2] = ERROR,
    [absolutly_Fin_Comment][fin_Comment] = ERROR,
    [absolutly_Fin_Comment][absolutly_Fin_Comment] = ERROR,
    [absolutly_Fin_Comment][string1] = ERROR,
    [absolutly_Fin_Comment][string2] = ERROR,
    //--------------------------------------------
    [string1][no_Comment] = TRUE,
    [string1][pre_Comment] = ERROR,
    [string1][comment_TYPE1] = ERROR,
    [string1][comment_TYPE2] = ERROR,
    [string1][fin_Comment] = ERROR,
    [no_Comment][absolutly_Fin_Comment] = ERROR,
    [string1][string1] = TRUE,
    [string1][string2] = ERROR,
    //--------------------------------------------
    [string2][no_Comment] = TRUE,
    [string2][pre_Comment] = ERROR,
    [string2][comment_TYPE1] = ERROR,
    [string2][comment_TYPE2] = ERROR,
    [string2][fin_Comment] = ERROR,
    [no_Comment][absolutly_Fin_Comment] = ERROR,
    [string2][string1] = ERROR,
    [string2][string2] = TRUE
    //--------------------------------------------
};
enum symbols translate(char symbol);
void read_And_Rewrite_File(FILE *Input_File, FILE *Output_File);

int main()
{
    FILE *input_File = NULL;
    FILE *output_File = NULL;
    if ((NULL == (input_File = fopen("input.txt", "r"))) || (NULL == (output_File = fopen("output.txt", "w")))) {
        puts("W A R N I N G ! ! !\a\nFile input.txt could not be opened!");
    }
    else {
        read_And_Rewrite_File(input_File, output_File);
    }
 
    fclose(input_File);
    fclose(output_File);
    return 0;
}
 
void read_And_Rewrite_File(FILE *Input_File, FILE *Output_File)
{
    //------------------------------------------------------Определям предыдущее состояние так, чтобы функция первый раз прошла по for в холостую, придя к верным значениям
    enum states current_State = no_Comment;
    enum states last_State = comment_TYPE1;
    //------------------------------------------------------Начинаем сам цикл, выводим предыдущее считанное значение (или не выводим), чтобы все работало правильно
    char last_Symbol = 0;
    for (char current_Symbol = getc(Input_File); current_Symbol != EOF; last_Symbol = current_Symbol, current_Symbol = getc(Input_File)) {
        enum symbols enum_Symbol = translate(current_Symbol);
        current_State = table_For_States[current_State][enum_Symbol];
        //------------------------------------------------------Определив предыдущее и текущее состояние можем по таблице узнать, можно ли записывать данные в файл
        if (table_For_Output[last_State][current_State]) {
            putc(last_Symbol, Output_File);
        }
        last_State = current_State;
        //------------------------------------------------------
    }
    //------------------------------------------------------Финальная запись, необходима, чтобы записать последние данные, без нее пропадут данные
    putc(last_Symbol, Output_File);
}
 
enum symbols translate(char symbol)
{
    switch (symbol)
    {
    case '/':
        return SLASH;
    case '*':
        return STAR;
    case '\'':
        return SINGLE_QUOTES;
    case '"':
        return DOUBLE_QUOTES;
    case '\n':
        return INDENT;
    default:
        return ANOTHER;
    }
}
 
#ifdef QWERTYU
------------------------------------------------Если ввести в файли input.txt эти данные
#include <stdio.h>
 
//just comment 1
/*just comment 2*/
if (1); //just comment 3
if (1); /*just comment 4*/
/*just comment 5*/if (1);
/*
if (1); just comment 6
*/
'//just comment '
// 'just comment '
"//just comment "
// "just comment "
'/*just comment */'
/*'just comment '*/
"/*just comment */"
/*"just comment "*/
 
'/*just comment */'
/*'just
comment '*/
"/*just comment */"
/*"just
comment "*/
int x = 4 / 2;
-------------------------------------------------То, после работы программы, в файле output.txt должны быть следующие данные
#include <stdio.h>

if (1);
if (1);
if (1);
 
'//just comment '
 
"//just comment "
 
'/*just comment */'
 
"/*just comment */"

'/*just comment */'
 
"/*just comment */"
 
int x = 4 / 2;
#endif
Однако, мне кажется она громоздкой. Может бывалые программисты, если есть время и интересна данная тема, предложат мне, как бы они оптимизировали данный код. Может еще что-нибудь подскажут. Буду премного благодарен. P.S. Ну и конечно же меня интересует, лучше ли и понятней код написанный с автоматом, или обычный код:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
 
void read_And_Rewrite_File(FILE *Input_File, FILE *Output_File);
 
int main()
{   
    FILE *input_File = NULL;
    FILE *output_File = NULL;
    if ((NULL == (input_File = fopen("input.txt", "r"))) || (NULL == (output_File = fopen("output.txt", "w")))) {
        puts("W A R N I N G ! ! !\a\nFile input.txt could not be opened!");
    }
    else {
        read_And_Rewrite_File(input_File, output_File);
    }
 
    fclose(input_File);
    fclose(output_File);
    return 0;
}
 
void read_And_Rewrite_File(FILE *Input_File, FILE *Output_File)
{
    char current_Symbol = getc(Input_File);;
    char last_Symbol = 0;
    char next_Symbol = getc(Input_File);
    bool is_String = false;
    for(;;) {
        //--------------------------------------------------Определяем комментарий типа /**/
        if (('/' == current_Symbol) && ('*' == next_Symbol) && (!is_String)) {
            for (;;) {
                last_Symbol = current_Symbol;
                current_Symbol = next_Symbol;
                next_Symbol = getc(Input_File);
                if ((last_Symbol == '*') && (current_Symbol == '/')) {
                    last_Symbol = current_Symbol;
                    current_Symbol = next_Symbol;
                    next_Symbol = getc(Input_File);
                    break;
                }
            }
        }
        //--------------------------------------------------Определяем комментарий типа //
        if (('/' == current_Symbol) && ('/' == next_Symbol) && (!is_String)) {
            while (current_Symbol != '\n') {
                current_Symbol = next_Symbol;
                next_Symbol = getc(Input_File);
            }
        }
        //--------------------------------------------------Позволяет не удалять комментарии в кавычках (39 - ASCII код одинарных кавычек)
        if (('"' == current_Symbol) && (!is_String)) {
            is_String = true;
        }
        else if (('\'' == current_Symbol) && (!is_String)) {
            is_String = true;
        }
        else if (('"' == current_Symbol) && (is_String)) {
            is_String = false;
        }
        else if (('\'' == current_Symbol) && (is_String)) {
            is_String = false;
        }
        //--------------------------------------------------
        //--------------------------------------------------
        if (EOF == next_Symbol) break;
 
        putc(current_Symbol, Output_File);
 
        last_Symbol = current_Symbol;
        current_Symbol = next_Symbol;
        next_Symbol = getc(Input_File);
        //--------------------------------------------------
    }
}

Решение задачи: «Конечный автомат на чистом С»

textual
Листинг программы
#include <stdio.h>
 
enum states
{
    state_text,
    state_str1,
    state_str2,
    state_com_begin,
    state_com1_inner,
    state_com2_inner,
    state_com2_out
};
 
enum str_types
{
    STR_TYPE_1 = '\'',
    STR_TYPE_2 = '"'
};
 
int main(int argc, char *argv[])
{
    int ch;
    enum states state = state_text;
    FILE *in = fopen(argv[1], "r+"), *out = fopen("out.txt", "w+");
    
    if(!in || !out)
        return 1;
 
    while((ch = fgetc(in)) != EOF) {
        switch(state) {
            case state_text:
                switch(ch) {
                    case '/':
                        state = state_com_begin;
                        break;
 
                    case STR_TYPE_1:
                        fputc(ch, out);
                        state = state_str1;
                        break;
 
                    case STR_TYPE_2:
                        fputc(ch, out);
                        state = state_str2;
                        break;
 
                    default:
                        fputc(ch, out);
                        break;
                }
                break;
 
            case state_str1:
                switch(ch) {
                    case STR_TYPE_1:
                        state = state_text;
                        /* Fall through */
 
                    default:
                        fputc(ch, out);
                        break;
                }
                break;
 
            case state_str2:
                switch(ch) {
                    case STR_TYPE_2:
                        state = state_text;
                        /* Fall through */
 
                    default:
                        fputc(ch, out);
                        break;
                }
                break;
 
            case state_com_begin:
                switch(ch) {
                    case '/':
                        state = state_com1_inner;
                        break;
 
                    case '*':
                        state = state_com2_inner;
                        break;
 
                    default:
                        fputc('/', out);
                        fputc(ch, out);
                        state = state_text;
                        break;                    
                }
                break;
 
            case state_com1_inner:
                switch(ch) {
                    case '\n':
                        fputc(ch, out);
                        state = state_text;
                        break;
                }
                break;
 
            case state_com2_inner:
                switch(ch) {
                    case '*':
                        state = state_com2_out;
                        break;
                }
                break;
 
            case state_com2_out:
                switch(ch) {
                    case '/':
                        state = state_text;
                        break;
                }
                break;
        }
    }
    fclose(in);
    fclose(out);
 
    return 0;
}

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

  1. В начале кода объявлены две константы enum states и enum str_types.
  2. Затем определена функция main, которая является точкой входа в программу.
  3. Внутри функции main происходит открытие файла для чтения и записи с помощью функции fopen.
  4. Если файлы не открылись, программа возвращает 1.
  5. В основной цикл программы входит while, который выполняется до тех пор, пока не достигнут конец файла (EOF).
  6. Внутри цикла происходит считывание символа из файла с помощью функции fgetc.
  7. Символ затем используется для определения текущего состояния конечного автомата.
  8. Состояние конечного автомата определяется с помощью оператора switch.
  9. В зависимости от текущего состояния и считанного символа выполняются различные действия.
  10. Действия включают запись символа в выходной файл и изменение состояния конечного автомата.
  11. Если считанный символ является символом новой строки, выполняется переход в состояние state_text.
  12. Если считанный символ является символом '/', выполняется переход в состояние state_com_begin.
  13. Если считанный символ является символом '*', выполняется переход в состояние state_com2_inner.
  14. Если считанный символ является символом '\'', выполняется переход в состояние state_com1_inner.
  15. Если считанный символ является символом '`', выполняется переход в состояние state_com2_out.
  16. Если считанный символ не соответствует ни одному из вышеперечисленных, он записывается в выходной файл.
  17. После завершения цикла программа закрывает файлы с помощью функции fclose.
  18. Программа возвращает 0, указывая, что все прошло успешно.

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


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

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

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