Конечный автомат на чистом С - C (СИ)
Формулировка задачи:
Здравствуйте, уважаемые!
Недавно предложили мне для выполнения задание по фильтрации текста, а именно удалить из текста комментарии. Когда же я с ним справился обычным способом(при помощи if, else) предложили справиться иным. Дали прочитать данную статью https://habrahabr.ru/post/241941/
и предложили методом, описываемым в оной статье избавиться от комментариев из файла.
На данный момент эта программа есть, и она работает:
Однако, мне кажется она громоздкой. Может бывалые программисты, если есть время и интересна данная тема, предложат мне, как бы они оптимизировали данный код. Может еще что-нибудь подскажут. Буду премного благодарен.
P.S. Ну и конечно же меня интересует, лучше ли и понятней код написанный с автоматом, или обычный код:
#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
#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; }
Объяснение кода листинга программы
- В начале кода объявлены две константы enum states и enum str_types.
- Затем определена функция main, которая является точкой входа в программу.
- Внутри функции main происходит открытие файла для чтения и записи с помощью функции fopen.
- Если файлы не открылись, программа возвращает 1.
- В основной цикл программы входит while, который выполняется до тех пор, пока не достигнут конец файла (EOF).
- Внутри цикла происходит считывание символа из файла с помощью функции fgetc.
- Символ затем используется для определения текущего состояния конечного автомата.
- Состояние конечного автомата определяется с помощью оператора switch.
- В зависимости от текущего состояния и считанного символа выполняются различные действия.
- Действия включают запись символа в выходной файл и изменение состояния конечного автомата.
- Если считанный символ является символом новой строки, выполняется переход в состояние state_text.
- Если считанный символ является символом '/', выполняется переход в состояние state_com_begin.
- Если считанный символ является символом '*', выполняется переход в состояние state_com2_inner.
- Если считанный символ является символом '\'', выполняется переход в состояние state_com1_inner.
- Если считанный символ является символом '`', выполняется переход в состояние state_com2_out.
- Если считанный символ не соответствует ни одному из вышеперечисленных, он записывается в выходной файл.
- После завершения цикла программа закрывает файлы с помощью функции fclose.
- Программа возвращает 0, указывая, что все прошло успешно.
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д