Избитая банальность. Как школьник бота писал

от автора

Мне 16 и я школьник. Не так уж давно меня посетила идея написать бота… Нет, PHP-поделие, уныло висящее на никому не нужном сайте. И даже не бесполезный ответчик на фразы типа "! Погода".

Бот задумывался для развлечения как «говорилка» на десктоп. Ужасно, правда? Но мне хочется узнать свои ошибки, ведь я ни разу не показывал свой код кому-либо, в школе только паскаль. Итак, следуя ненавидимому некоторыми чистому структурному подходу я написал первоначальный вариант на C++.

Задумка такова. Бот берет фразу из консоли, вычленяет слова, проверяет каждое по словарю, расположенному в файле «Memory.txt», и возвращает найденный ответ к каждому слову; если ни на одно слово ответ не найден, то он возвращает оговоренную фразу (не принципиально).

Словарь в файле «Memory.txt» структурирован простейшим образом:
слово=ответ

Пример:
яблоко=яблоки вкусные

Bot.h- заголовочный файл, о нем позже. Основные функции будут располагаться в файле Bot.cpp:

/** Даниил Демидко, 2015 Основные функции Cbot */ #include"Bot.h" 

Определим имя для словаря в этом же файле:

///Имя тектового файла- памяти бота const char *const MemoryPath="Memory.txt"; 

«Основа основ» бота — это функция, выделяющая слова из одной строки в массив строк и возвращающая указатель на массив.

const std::string *const GetWords(const std::string &Word)

    ///Количество возвращаемых слов в массиве-глобальная переменная, надеюсь всем понятно почему...     int MaxIndex=0;     ///Функция выделяет слова из строки     const std::string *const GetWords(const std::string &Word)     {         ///Резервируется массив в куче на 256 слов         std::string *const PtrWords=new std::string[256];         ///Сбрасываем предидущее значение         MaxIndex=0;         ///Фиксирует наличие искомого символа в предидущих циклах         bool Fix=false;         ///Последний символ- служебный, поэтому не учитыватся         for(int i=0; i<Word.size(); ++i)         {             ///Символы- разделители слов         if(Word[i]==' '||Word[i]=='.'||Word[i]==','||Word[i]=='!'||Word[i]=='?'||Word[i]=='='||Word[i]=='/')             {                 ///При нахождении разделителя, фиксируем это и пропускаем один цикл                 Fix=true;                 continue;             }             ///Если в предидущих циклах зафиксирован разделитель, то переходим на одну ячейку             if(Fix)             {                 Fix=false;                 ++MaxIndex;             }             PtrWords[MaxIndex]+=Word[i];         }         return PtrWords;     } 

Следующая функция получает строку для поиска и ищет ее по словарю, если ответ не найден, возвращает пустую строку "". Хочу обратить внимание на то, что если слово будет найдено внутри другого слова в файле ассоциаций, то ответ будет засчитан.

const std::string GetAssociation(const std::string &Word)

///Функция возвращает ассоциацию     const std::string GetAssociation(const std::string &Word)     {         std::ifstream Memory(MemoryPath, std::ios::in);         if(!Memory)         {             std::ofstream NewMemory(MemoryPath);             NewMemory.close();             Memory.open(MemoryPath);             return "";         }         while(!Memory.eof())         {             std::string Buffer="";             std::getline(Memory, Buffer);             if(Buffer.find(Word)!=-1)             {                 std::string Result[2];                 for(int i=0, Index=0; i<Buffer.size(); ++i)                 {                     if(Buffer[i]=='=')                     {                         ///Символы после второго знака '=' включительно- игнорируются                         if(Index==1)                         {                             break;                         }                         ++Index;                         continue;                     }                     Result[Index]+=Buffer[i];                 }                 if(Result[0].find(Word)!=-1)                 {                     Memory.close();                     return Result[1];                 }             }         }         Memory.close();         return "";     } 

Теперь можно подумать о необязательное мелочи- ужасной пародии на обучение- добавление новых ассоциаций при нахождении в строке символа ‘-‘.
Пример:
Зло- это добро наоборот
В словарь идет:
Зло= это добро наоборот
Не забываем о том, что при нахождении слова внутри другого, ответ засчитывается, так что результат может быть интересным.

void PutAssociation(const std::string &Left, const std::string &Right)

///Функция добавляет ассоциацию     void PutAssociation(const std::string &Left, const std::string &Right)     {         std::ofstream Memory(MemoryPath, std::ios::app);         Memory<<Left<<'='<<Right<<std::endl;         Memory.close();     } 

В моем представлении структурный подход не отменяет инкапсуляцию, поэтому мы добавим анонимное пространство имен — для банальной инкапсуляции включающее в себя все предыдущие функции.

Таким образом предыдущие функции уже не будут доступны при подключении заголовочного файла «Bot.h». Позволю себе сослаться на гуру:

Это самый передовой способ избежать объявления функций и переменных со статическим связыванием.
Доступ может быть осуществлен только в рамках единицы
трансляции (т. е. в полученном после предварительной обработки файле),
в которой они находятся, точно так же, как к статическим переменным.
Стивен С. Дьюрхест, «C++. Священные знания»

Вот, все вместе:

namespace

namespace {     ///Имя тектового файла- памяти бота     const char *const MemoryPath="Memory.txt";     ///Количество возвращаемых слов в массиве     int MaxIndex=0;     ///Функция выделяет слова из строки     const std::string *const GetWords(const std::string &Word)     {         ///Резервируется массив на 256 слов         std::string *const PtrWords=new std::string[256];         ///Сбрасываем предидущее значение         MaxIndex=0;         ///Фиксирует наличие искомого символа в предидущих циклах         bool Fix=false;         ///Последний символ- служебный, поэтому не учитыватся         for(int i=0; i<Word.size(); ++i)         {             ///Символы- разделители слов             if(Word[i]==' '||Word[i]=='.'||Word[i]==','||Word[i]=='!'||Word[i]=='?'||Word[i]=='='||Word[i]=='/')             {                 ///При нахождении разделителя, фиксируем это и пропускаем один цикл                 Fix=true;                 continue;             }             ///Если в предидущих циклах зафиксирован разделитель, то переходим на одну ячейку             if(Fix)             {                 Fix=false;                 ++MaxIndex;             }             PtrWords[MaxIndex]+=Word[i];         }         return PtrWords;     }          ///Функция добавляет ассоциацию     void PutAssociation(const std::string &Left, const std::string &Right)     {         std::ofstream Memory(MemoryPath, std::ios::app);         Memory<<Left<<'='<<Right<<std::endl;         Memory.close();     } } 

И наконец, функция для связи с внешним миром, разумеется вне пространства имен, но в той же единице компиляции. Принимает фразу, вычленяет слова, получает ассоциации по каждому, при нахождении символа равно добавляет новую ассоциацию при помощи предыдущих функций:

const std::string GetFullAssociation(const std::string &Word)

///Возвращает ассоциации по всем словам фразы const std::string GetFullAssociation(const std::string &Word) {     const std::string *const Words=GetWords(Word);     std::string Result="";     for(int i=0; i<=MaxIndex; ++i)     {         const std::string Buffer=GetAssociation(Words[i]);         if(Buffer!="")         {             Result+=Buffer+' ';         }     }     delete[] Words;     if(Word.find('-')!=-1)     {         std::string NewAssociations[2];         for(int i=0, Index=0; i<Word.size(); ++i)         {             if(Word[i]=='-')             {                 if(Index==1)                 {                     break;                 }                 ++Index;                 continue;             }             if(Word[i]=='=')             {                 continue;             }             NewAssociations[Index]+=Word[i];         }         PutAssociation(NewAssociations[0], NewAssociations[1]);     }     return Result; } 

А теперь подведем итог — файл «Bot.cpp» полностью:

Bot.cpp

/** Даниил Демидко, 2015 Основные функции Cbot */ #include"Bot.h" ///Анонимное пространство имен инкапсулирует функции за пределами этой единицы компиляции namespace {     ///Имя тектового файла- памяти бота     const char *const MemoryPath="Memory.txt";     ///Количество возвращаемых слов в массиве     int MaxIndex=0;     ///Функция выделяет слова из строки     const std::string *const GetWords(const std::string &Word)     {         ///Резервируется массив на 256 слов         std::string *const PtrWords=new std::string[256];         ///Сбрасываем предидущее значение         MaxIndex=0;         ///Фиксирует наличие искомого символа в предидущих циклах         bool Fix=false;         ///Последний символ- служебный, поэтому не учитыватся         for(int i=0; i<Word.size(); ++i)         {             ///Символы- разделители слов             if(Word[i]==' '||Word[i]=='.'||Word[i]==','||Word[i]=='!'||Word[i]=='?'||Word[i]=='='||Word[i]=='/')             {                 ///При нахождении разделителя, фиксируем это и пропускаем один цикл                 Fix=true;                 continue;             }             ///Если в предидущих циклах зафиксирован разделитель, то переходим на одну ячейку             if(Fix)             {                 Fix=false;                 ++MaxIndex;             }             PtrWords[MaxIndex]+=Word[i];         }         return PtrWords;     }     ///Функция возвращает ассоциацию     const std::string GetAssociation(const std::string &Word)     {         std::ifstream Memory(MemoryPath, std::ios::in);         if(!Memory)         {             std::ofstream NewMemory(MemoryPath);             NewMemory.close();             Memory.open(MemoryPath);             return "";         }         while(!Memory.eof())         {             std::string Buffer="";             std::getline(Memory, Buffer);             if(Buffer.find(Word)!=-1)             {                 std::string Result[2];                 for(int i=0, Index=0; i<Buffer.size(); ++i)                 {                     if(Buffer[i]=='=')                     {                         ///Символы после второго знака '=' включительно- игнорируются                         if(Index==1)                         {                             break;                         }                         ++Index;                         continue;                     }                     Result[Index]+=Buffer[i];                 }                 if(Result[0].find(Word)!=-1)                 {                     Memory.close();                     return Result[1];                 }             }         }         Memory.close();         return "";     }     ///Функция добавляет ассоциацию     void PutAssociation(const std::string &Left, const std::string &Right)     {         std::ofstream Memory(MemoryPath, std::ios::app);         Memory<<Left<<'='<<Right<<std::endl;         Memory.close();     } } ///Возвращает ассоциации по всем словам фразы const std::string GetFullAssociation(const std::string &Word) {     const std::string *const Words=GetWords(Word);     std::string Result="";     for(int i=0; i<=MaxIndex; ++i)     {         const std::string Buffer=GetAssociation(Words[i]);         if(Buffer!="")         {             Result+=Buffer+' ';         }     }     delete[] Words;     if(Word.find('-')!=-1)     {         std::string NewAssociations[2];         for(int i=0, Index=0; i<Word.size(); ++i)         {             if(Word[i]=='-')             {                 if(Index==1)                 {                     break;                 }                 ++Index;                 continue;             }             if(Word[i]=='=')             {                 continue;             }             NewAssociations[Index]+=Word[i];         }         PutAssociation(NewAssociations[0], NewAssociations[1]);     }     return Result; } 

Вот и все с файлом «Bot.cpp» мы закончили, теперь быстро набросаем заголовочный файл «Bot.h»:

Bot.h

#ifndef BOT #define BOT ///На всякий случай проверяем, подключен ли уже iostream #ifndef _GLIBCXX_IOSTREAM #include<iostream> #endif            //_GLIBCXX_IOSTREAM ///Проверяем fstream #ifndef _GLIBCXX_FSTREAM #include<fstream> #endif           //_GLIBCXX_FSTREAM ///Наша уже описанная функция для связи с миром extern const std::string GetFullAssociation(const std::string&); #endif          //BOT 

С основной частью мы закончили, дело за малостью — функцией main(). Она у нас будет располагаться в файле Cbot.cpp. Cbot — звучит невероятно оригинально, ведь правда?

Cbot.cpp

#include"Bot.h" int main() {     ///Файл кодировки 866 OEM (русская), файл "Memory.txt" должен быть в ней же     setlocale(LC_ALL, ".866");     std::wcout<<"Cbot 2.0\nАвтор: Даниил Демидко\nE-Mail: DDemidko1@gmail.com"<<std::endl;     while(true)     {         std::wcout<<"Я: ";         std::string Buffer="";         std::getline(std::cin, Buffer);         const std::string Association=GetFullAssociation(Buffer);         /**         Почему такая конструкция? Казалось, ведь можно было бы проще-          if(Association=="")         {             Association="Bot: Не распознана ключевая последовательность!";         }         std::cout<<Association<<std::endl;          Но мы не должны забывать, что работаем с 866 OEM-  с ней не получится корректно присвоить объекту std::string строку кириллических символов прямо из кода-         такое возможно (с 866 OEM) только при вводе из консоли.         */         if(Association=="")         {             std::wcout<<"Bot: Не распознана ключевая последовательность!"<<std::endl;         }         else         {             std::cout<<"Bot: "<<Association<<std::endl;         }     } } 

Все, бот готов, собираем вместе, получаем Cbot.exe, сохраняем файл Memory.txt в кодировке OEM 866 и кладем в одну директорию программой. Ссылка на сборку: spaces.ru/files/?r=main/view&Read=58688510

Ожидаю конструктивный поток критики показывающий на очевидные ошибки в коде.

ссылка на оригинал статьи http://habrahabr.ru/post/266351/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *