Мне 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

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

Комментарии (44)


  1. Hertz
    07.09.2015 15:08
    +7

    Стоило использовать std::vector<std::string> вместо сырого владеющего указателя, не хочется вектор — тогда std::unique_ptr<std::string[]>.

    if(Word.find('-')!=-1)
    

    Cтоит использовать std::string_type::npos, эта константа не обязана быть -1.
    Использование const на возвращаемом по значению типе предотвращает всякие оптимизации.


  1. Ruckus
    07.09.2015 15:15

    Интересно, почему именно С++? И почему такая странная кодировка?


    1. Kop3t3
      07.09.2015 19:40
      +2

      Кодировка потому, что Windows. (там консоль работает в отличной от всего остального кодировке)
      Хотя лично я до перехода на *nix использовал для вывода libiconv.

      Грязный хак для libiconv
      #include "iconv/iconv.h"
      
      std::string convert(const std::string& str, iconv_t conv)
      {
      	iconv(conv, 0, 0, 0, 0);
      
      	const size_t buffer_size = 512;  
      	char out[buffer_size];
      	
      	const char* in_buf = str.c_str();
      	size_t in_size = str.size();
      	
      	std::string result;
      	
      	while (in_size > 0)
      	{
      		char* out_buf = out;
      		size_t out_size = buffer_size;
      	
      		int n = iconv(conv, &in_buf, &in_size, &out_buf, &out_size);
      		if (n < 0)
      		{
      			if (errno != E2BIG) break;
      		}
      		result.append(out, buffer_size - out_size);
      	}
      	
      	return result;
      }
      
      
      char *Win2Dos(char *in)
      {
           iconv_t ic =iconv_open("CP866","windows-1251");
           const std::string _a = (std::string)in;
           char *_o = (char *)convert(_a,ic).c_str();
           iconv_close(ic);
           return _o;
      }
      
      char *Dos2Win(char *in)
      {
           iconv_t ic =iconv_open("windows-1251","CP866");
           const std::string _a = (std::string)in;
           char *_o = (char *)convert(_a,ic).c_str();
           iconv_close(ic);
           return _o;
      }
      


      1. Ruckus
        08.09.2015 09:18
        +2

        Это ужасно… Windows до сих пор не знает что такое UTF8, а дети программисты пишут на C++ то, что стоило бы писать на скриптовом языке. Для обучения неплохо и, если вы школьник, даже круто, я таким в школе похвастаться не мог. Продолжайте в том же духе.
        P.S. Но я бы автору рекомендовал посмотреть в сторону чего-нибудь попроще: Python, Ruby, Java. Ну или в сторону Qt если очень нравится C++. Вы получите как минимум кроссплатформенность (и никаких больше OEM 866) и гораздо более простую работу со строками (по крайней мере в Python). Но решать конечно вам.


        1. fareloz
          08.09.2015 10:49
          +2

          А я бы не советовал. Миру также нужны люди, которые будут разбираться в извращениях. Ведь как раз они и пишут, например, Qt, который скрывает за своим интерфейсом все это. Поэтому пешать действительно автору.


        1. ice2heart
          10.09.2015 08:16
          +1

          Если честно с qt и виндовой консолью тоже не все так просто.
          Я тоже за qt очень приятная штука.
          Хотя если жить с плюсами без boost не обойтись порой.


        1. Daniro_San
          10.09.2015 10:21

          Qt как слишком огромен что бы привлекать его для такой простой задачи.
          Да и где логика- использовать 2гигабитный фреймверк со своими велосипедами почти для всего
          ради одного маленького консольного приложения?
          Python мне не нравится из за синтаксиса (да да, вложенность) + это не компилируемый язык.
          Java на десктопе ужасна не слишком быстра и создавалась она уж точно не для такого.
          Ruby пугает своим ООП + Ruby поднялся только благодаря рельсам, благодаря им же и упал.
          Вот и остается (Заметьте, для такой задачи) чистый C++ без тотального ООП, обязательного для всех стиля,
          промежуточной компиляции и всего прочего, присущего тому что вы перечислили.

          Впрочем, каждому свое)
          Насчет строк не знаю, а как калькулятор Python вполне пригоден)


          1. fareloz
            10.09.2015 10:33

            Забыли C#. Вполне подходит.


            1. Daniro_San
              10.09.2015 10:53

              Да, но промежуточная компиляция никуда не уходит. И можно забыть о UNIX


              1. Ruckus
                10.09.2015 10:58
                +1

                О *nix вы уже забыли с вашей кодировкой. Тот код, что вы воложили, будет нормально работать только на Windows.


                1. Daniro_San
                  10.09.2015 11:43

                  Насколько я знаю в *nix проблема с кодировками вообще не стоит так. Убираем setlocale и радуемся жизни.
                  Насчет Mono- много ли вы знаете людей постоянно использующих Mono с *nix?
                  Кроме разработчиков Mono конечно.
                  Всегда казалось, что C# — язык ориентированный на разработку в Windows.
                  Поэтому для наглядности решил использовать C++ при написании статьи)


                  1. Ruckus
                    10.09.2015 11:56

                    По поводу кодировки и *nix ничего не могу сказать, проблем быть не должно, но вероятно будут. Вообще в C и C++ лично для меня сложно что-то предсказывать про другие платформы, особенно в плане строк. Было дело пытались создать файл с русским именем и в него что-то на русском написать, с одной кодировкой в одном месте иероглифы, с другой в другом, как справились не помню, но решение искали втроём около часа. Так же делал Qt проект, под *nix'ами он собирался и работал как надо, а под Windows был Segmentation fault, очень долго пришлось всё отлаживать. С тех пор с C и C++ стараюсь не связываться, хоть иногда и приходится.

                    По поводу Mono я вообще ничего не знаю, как и о C#. Мне всегда, так же как и вам, казалось, что язык нацелен на Windows и кросыплатформенность у него посредственная, а у меня Windows только в виртуалке.


              1. fareloz
                10.09.2015 11:00
                +1

                Уже было тут две статьи по поводу «кто быстрее» — плюсы или шарп. ИМХО — у Вас программа настолько мала, что даже самый большой оверхед на C# не будет ощутим даже на самой плохенбкой системе. Кроме того для UNIX, если я не ошибься, Microsoft недавно начала поддерживать Mono.


            1. Daniro_San
              10.09.2015 10:54

              И кстати, GUI для бота я написал как раз на C#+WPF


              1. fareloz
                10.09.2015 10:58

                В ветке идет обсуждение «почему С++, ведь у него строки», а не про интерфейс. Поэтому и предложил все на шарпе.


                1. Daniro_San
                  10.09.2015 11:38

                  Почему вам кажется, что в C# работа со строками (в этом случае) легче чем в C++?
                  Ну хорошо, size() заменим на Length, а дальше?

                  Аналогичный код на C# специально для вас
                  static class Bot{
                  	public static readonly System.String MemoryPath="Memory.txt";
                  	private static System.String[] GetWords(System.String Word){
                  		//Подсчитывает слова для создания массива
                  		//Не забываем, что последний символ массива- служебный, поэтому 1, а не 0
                  	    //В 0 изначально уходит строка
                  		System.Int32 MaxIndex=1;
                  		//Фиксирует наличие в предидущих циклах символов-разделителей
                  		System.Boolean SeeFix=false;
                  		for(System.Int32 i=0; i<Word.Length; ++i){
                  			if(Word[i]==' '||Word[i]=='.'||Word[i]==','||Word[i]=='!'||Word[i]=='?'||Word[i]=='='||Word[i]=='/'||Word[i]=='\n'){
                  				SeeFix=true;
                  				continue;
                  			}
                  			if(SeeFix){
                  				SeeFix=false;
                  				++MaxIndex;
                  			}
                  		}
                  		System.String[] ArrWords=new System.String[MaxIndex];
                  		System.Boolean Fix=false;
                  		for(System.Int32 i=0, ThisIndex=0; i<Word.Length; ++i){
                  			if(Word[i]==' '||Word[i]=='.'||Word[i]==','||Word[i]=='!'||Word[i]=='?'||Word[i]=='='||Word[i]=='\n'){
                  				Fix=true;
                  				continue;
                  			}
                  			if(Fix){
                  				Fix=false;
                  				++ThisIndex;
                  			}
                  			ArrWords[ThisIndex]+=Word[i];
                  		}
                  		return ArrWords;
                  	}
                  	private static System.String GetAssociation(System.String Word){
                  		System.IO.StreamReader Reader=System.IO.File.OpenText(MemoryPath);
                  		while(true)
                  		{
                  			System.String Buffer=Reader.ReadLine();
                  			if(Buffer==null){
                  				break;
                  			}
                  			if(Buffer.Contains(Word)){
                  				//Резервируем массив из двух слов для левой и правой ассоциаций
                  				System.String[] Result=new System.String[2];
                  				for(System.Int32 i=0, Index=0; i<Buffer.Length; ++i){
                  					if(Buffer[i]=='='){
                  	//Сиволы следующие после второго в строке символа '=' игнорируются
                  						if(Index==1){
                  							break;
                  						}
                  						++Index;
                  						continue;
                  					}
                  					Result[Index]+=Buffer[i];
                  				}
                  //Проверяем на содержание искомой строки только левую ассоциацию,
                  //При успехе возвращаем правую ассоциацию
                  				if(Result[0].Contains(Word)){
                  					Reader.Close();
                  					return Result[1];
                  				}
                  			}
                  		}
                  		Reader.Close();
                  	    return null;
                  	}
                  	private static void PutAssociation(System.String Left, System.String Right){
                  		System.IO.File.AppendAllText(MemoryPath, Left+'='+Right+'\n');
                  	}
                  	public static System.String GetFullAssociation(System.String Word){
                  		if(Word==null){
                  			return null;
                  		}
                  		System.String[] Words=GetWords(Word);
                  		System.String Result=null;
                  		System.Boolean FixResult=false;
                  		for(System.Int32 i=0; i<Words.Length; ++i){
                  			if(GetAssociation(Words[i])!=null){
                  				FixResult=true;
                  				Result+=Bot.GetAssociation(Words[i])+' ';
                  			}
                  		}
                  		if(!FixResult){
                  			Result=null;
                  		}
                  		if(Word.Contains("-")){
                  			System.String[] NewAssociations=new System.String[2];
                  			for(System.Int32 i=0, Index=0; i<Word.Length; ++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;
                  	}
                  }
                  


                  1. fareloz
                    10.09.2015 14:48

                    Почитайте первые два комментария в ветке. Мы о кодировках.


              1. Ruckus
                10.09.2015 10:59
                +1

                А комментарием выше пишите что-то про *nix. Хм…


          1. Ruckus
            10.09.2015 10:56

            Qt не мал, но на нём хотябы писать можно, на чистых плюсах разве что серверные решения писать. Где вы там 2 гигабИта нашли я не знаю, вообще единицы измерения у вас не менее странные, чем кодировка, у меня выходило около 50-100МБ библиотек ссобой, что тоже не мало, но в современном мире особой роли не играют.

            Синтаксис Python как говорится на вкус и цвет, но вот про компилируемость и «калькулятор» я совсем не понял. Зачем вам компилируемость? Что вы хотели сказать скрытым текстом? Ну да ладно, дело ваше. То что на этом языке ваша программа сократится раз в 10, а по скорости будет такая же плюс отладка в разы проще это впринципе не существенно. То что упаковать в .exe можно тоже, помимо того будет работать одинаково хорошо везде где есть интерпретатор питона. В общем много фич, но это не важно.

            Java, на удивление, частично вытеснила C и C++ из серверного сегмента, а вы говорите, что она на десктопе медленная. Вы замеряли? Тесты просматривали хотябы? Почему не говорите, что скриптовые языки медленные, а про яву внезапно заявили? Знаете, но многие ресурсы вообще утверждают, что Java быстрей C++, вот, например.

            Хм. Ну если вы боитесь ООП, то ок. Мне ООП кажется более удобным и перспективным.

            А судя по вашим замечаниям создаётся ощущение, что вам не 16, а 61. Очень «устаревшие» взгляды, как по мне.


            1. ice2heart
              10.09.2015 11:03

              Последний Qt если собирать MSVC в 10-12 мегабайт укладывается (без некоторых модулей).
              Java щас больше в интерпрайзе, потому что покупной софт на джаве, а держать ещё и плюсовиков никому не хочется, вот и лезет в java. (зы по вашей ссылке я так и не увидел что java быстрее, а вот памяти точно сильно больше ест)


              1. Ruckus
                10.09.2015 11:21

                Прошу прощения, читал, что быстрее несколько в другом месте, а этот ресурс обычно привожу для сравнения скорости ЯП и по ошибке интерпретировал приведённые там данные наоборот. И тем не менее там есть тест в котором Java отработала быстрее. По памяти я давно уже не заморачиваюсь, в современных ПК её с запасом, хотя возможно стоило бы.

                А с Qt работал последний раз с 4.8-5.0 и с gcc/mingv. Если таскать ссобой заранее скомпилированные библиотеки, которые сам проект предоставляет у меня с GUI и network выходило около 80МБ. Сейчас возможно что-то поменялось, а по поводу MSVC вообще не знаю ибо работаю под Linux, дома Mac и так уже давно.


                1. Daniro_San
                  10.09.2015 11:49

                  Знаете, вам то может быть и удобно таскать с собой скомпилированные библиотеки из Qt по отдельности- а мне нет. Не вижу в этом смысла. Ну а насчет 2гб- загляните но оф сайт Qt
                  Что то мне подсказывает, что я еще и занизил цифры.


                  1. Ruckus
                    10.09.2015 12:08

                    Не совсем понял о чём вы. Если брать *nix, то там библиотеки ставятся в системе и такскать их ссобой вообще не надо. Если брать Windows, то для разработки, возможно, нужны все, но таскать с приложением можно только те, которые это приложение использует. В случае данного приложения хватит всего одной(если я ничего не путаю). Смысл лишь в том, чтоб софт работал и писать код этого софта было удобней, приятней и быстрее.
                    По объёму на оф. сайте сходу не нашёл, раньше весили в полном комплекте меньше 1ГБ включая отладочные, которые весят раза в 2 больше и нужны только для отладки. Исходники библиотек вообще не нужны в большинстве случаев.


                    1. Daniro_San
                      10.09.2015 12:20

                      А как насчет этого?
                      Я бы не хотел разбираться и возиться с Qt ради пары библиотек.image
                      А использование Qt без Qt Creator автоматически означает возню с настройкой IDE и компилятора под Qt.


                      1. Ruckus
                        10.09.2015 12:36

                        Возможно стоит ткнуть на стрелочки и посмотреть что он там ставит поподробней?
                        И последняя версия на данный момент 5.5, где вы взяли этот установщик?
                        P.S. И я конечно не исключаю, что что-то изменилось, но человек несколькими постами выше говорит об обратном.


                      1. Kop3t3
                        12.09.2015 07:01

                        Опять-таки, столько весит среда разработки, необходимые для работы софта либы весят меньше. Но ИМХО, чистых сей хватит — если мы всё ещё обсуждаем кодировки, то легче нагуглить (или написать) простенький конвертер кодировок. (Хотя, некоторые считают, что тут пары строчек безо всяких либ хватит)


                        1. Ruckus
                          12.09.2015 11:58

                          Гораздо приятней, чем 866, но всё ещё не кроссплатформенно (или я ошибаюсь?). А есть какой-нибудь вариант, чтобы во всех ОС работало нормально?

                          PS Я в институте всегда делал

                          setlocale(LC_ALL, "Russian");
                          

                          Для вывода вроде помогало, но для ввода непомню.


                          1. Kop3t3
                            12.09.2015 16:38

                            #ifdef _WIN32
                            setlocale(LC_ALL, "Russian");
                            #endif
                            

                            только при переносе текстового файла его придётся конвертировать.
                            Вообще, если цель — кроссплатформенность (её у топикстартера нет, но всё же) лучше сохранить файл в зависимости то того, какую ОС вы уважаете больше в кодировке CP1251 (под windows она по умолчанию, а под *nix открывать в редакторе типа gedit с выбором кодировки) или UTF-8 (всё наоборот, к слову, gedit под windows тоже есть). К проекту приделываем libiconv и делаем что-то типа такого:
                            #ifdef _WIN32
                            #define OUTPUT_ENCODING "cp866"
                            #endif
                            #ifdef __linux__
                            #define OUTPUT_ENCODING "utf-8"
                            #endif
                            


            1. Daniro_San
              10.09.2015 11:46

              Хорошо, где конкретно в этой задаче применимо ООП?
              Можно было бы создать статический класс (привет C#), инкапсулировать функции немного по другому, и это все?


            1. Daniro_San
              10.09.2015 11:58

              Насчет отладки- как мне казалось современные IDE давно решили эту проблему…
              И легкость (вами заявленная) в отладке- еще не повод выбирать Python


            1. Daniro_San
              10.09.2015 12:07

              Насчет Java.
              Разумеется, я просматривал тесты.
              Не видно, где это Java хоть немного на равных с C++.
              Да и элементарное рассуждение- jvm с ее не далеко лучшей интеграцией с системой будет явно уступать C++ везде где только можно.
              Кстати, в большинстве «сравнений C++ и Java» сравнения ведутся с примитивными типами, не с кучей, и к тому же Java оптимизирует код, компиляторы C++ в этом отношении ведут себя честнее.
              Я не против jvm, я просто не понимаю вашего беспокойства за Java)


              1. Ruckus
                10.09.2015 12:33

                Я не за Java. C++ тоже умеет делать и -O2 и -O3, так что в чём честность неясно.
                Я просто не понимаю почему C++ и почему вы называете его единственным вариантом для данной задачи.
                IDE не решают всех проблем, я уже ощутил это. Да и не все ими пользуются. На данный момент сравнивая все языки на которых я писал меньше всего мне нравятся C и C++ за черезмерную сложность, большое количество кода, сложную отладку (а пару лет назад мне так не казалось, но я просто не видел другого), необходимость прямой работы с памятью, очень много непредвиденных поведений (они конечно вытекают из документации, но запомнить и всегда держать в голове их все довольно сложно) и так далее. И как мне кажется все эти минусы оправданы только когда нам нужна очень высокая производительность, а в задаче, описанной в статье, сверхпроизводительности не требуется и разница между С++ и чем угодно другим будет незаметна.


                1. whunter
                  10.09.2015 15:01

                  Я наоборот полюбил С за возможность прямой работы с памятью (да, рассматриваю как фичу), и навык разбираться в непредвиденных поведениях. При этом не отрицаю полезности и функциональности других ЯП, считаю что они применимы в зависимости от задачи, это инструмент. Но для школьника считаю написание приложений на С\++ крайне полезным, несмотря на целесообразность в конкретной задаче.


            1. Daniro_San
              10.09.2015 12:16

              Насчет:

              Хм. Ну если вы боитесь ООП, то ок. Мне ООП кажется более удобным и перспективным

              Буду благодарен, если вы покажете мне, как улучшить код в данном случае используя ООП.
              Не нужно создавать систему с наследованием классов ради пары функций.
              Если вам так нравится уделять кучу времени решению абстрактных задач, построению иерархии классов, по ходу чего частенько появляется еще с 10ок проблем только из за ООП- то пожалуйста.
              Если _вам_так_нравится_Python_ опять же ваше _дело_.


              1. Ruckus
                10.09.2015 12:51

                Я лишь отвечаю на ваши комментарии и не могу понять ваш выбор. Что мне нравится это отдельная тема. Я лишь привожу доводы почему что-то лучше/хуже для тех или иных задач и пытаюсь от вас добиться доводов почему другие языки не могут решить ту же задачу с меньшими трудозатратами.

                Если бы вы написали, что обожаете C++, а остальные языки втопку, было бы понятней, но когда вы начинаете объяснять, что все остальные такие плохие, хочется донести до вас, что не всегда это так и не в этом случае точно, ну или хотябы для себя узнать чем же всётаки так хорош C++, возможно я его недооценивал.


        1. sand14
          10.09.2015 12:44

          Это ужасно… Windows до сих пор не знает что такое UTF8

          Windows отлично знает, что такое Unicode (UTF-16) и UTF-8.

          Но если вы наберете скрипт в блокноте, и решите сохранить его не в UTF-8 или UTF-16, а в однобайтной кодировке ANSI (в случае русифицированной Windows — Windows-1251), то при выводе неанглийских символов в консоли будет абракадабра, т.к. консоль интерпретирует однобайтные символы в кодировке ASCII (DOS) (в случае русифицированной Windows то DOS-866).

          программисты пишут на C++ то, что стоило бы писать на скриптовом языке

          Возможно, автор-школьник будет тем, кто пишет на C++ интерпретаторы скриптовых языков.
          На чем то нужно учиться.


  1. roman_kashitsyn
    07.09.2015 15:36
    +2

    Неясно, зачем нужны внешние инклюд-гарды в Bot.h.
    Более того, если кому-то нужны стримы, пусть заинклюдит их в соответствующем cpp-файле. В хедер-файле с одной функцией, не имеющей в сигнатуре стримов, им не место.


  1. chuikoffru
    07.09.2015 16:03

    Хм, спасибо за идею. Захотелось на Go сделать что-то подобное, только на узкую тему.


  1. maaGames
    07.09.2015 16:30
    +1

    > std::wcout<<«Bot: Не распознана ключевая последовательность!»

    Уточка говорит: «Зря-зря».

    По коду ничего говорить не буду, но дам пару рекомендаций для последующей реализации:
    1. В юникод потоки следует посылать юникод строки. Но сперва нужно подключить к ним русскую локаль, тогда можно на человеческом языке писать, без 866 кодировки.
    2. Перед сохранением конвертируй строки в utf8; при чтении — обратно в юникод.
    3. Про ASCII строки пора бы уже начать забывать.


  1. encyclopedist
    07.09.2015 19:56
    +2

    Помимо того, что уже сказали:

    — Вместо того, чтобы добавлять к слову по одному символу, лучше сразу определить начало и конец, а потом сконструировать строку из диапазона.

    — Правильный способ чтения файла по строкам это:

    std::string line;
    while (std::getline(Memory, line))
    {
        // ...
    }
    

    — Явно закрывать потоки нет необходимости, они автоматически закрываются в деструкторах. В этом вся сила RAII.
    — (Стилистика) Настоятельно рекомендую добавлять пробелов вокруг операторов, будет лучше читаться.
    — Ну и ещё раз, у вас какой-то ужас в заголовочном файле. Вам не нужно защищать чужие заголовки от повторного включения, это их задача. В заголовочнов файле должны быть включены только те заголовки, которые нужны именно в этом заголовочном файле, а не в cpp файле. Поскольку в объявлении функции используестя только std::string, то и включить следует только string. Для функции extern тут не нужен. Ну и как уже сказали, возвращать константное значение не стоит. Результат должен выглядеть вот так:
    #ifndef BOT
    #define BOT
    
    #include <string>
    
    std::string GetFullAssociation(const std::string&);
    
    #endif          //BOT
    



    1. Daniro_San
      08.09.2015 07:04

      Спасибо, насчет возврата константы вы пожалуй правы)
      Старался использовать const везде кроме тех случаев где его нельзя использовать, следуя советам из комментариев прочитанных ранее на хабре)


      1. Hertz
        08.09.2015 09:50
        +1

        const на user-defined типе, возвращаемом из функции, препятствует move-семантике, и никакой пользы не приносит. Поскольку может происходить копирование, этот const все равно ничего не дает. const (не top-level) имеет смысл при возврате из функции только если возвращается ссылочный тип (ссылка или указатель).


    1. Daniro_San
      08.09.2015 07:39

      И еще есть вопрос- почему extern здесь не нужен?
      Он нужен только в при объявлении прототипа из .cpp?


      1. Hertz
        08.09.2015 09:52
        +1

        Функции по-умолчанию имеют external linkage, именно чтобы избавиться от этого умолчания вы использовали anonymous namespace.