Бот задумывался для развлечения как «говорилка» на десктоп. Ужасно, правда? Но мне хочется узнать свои ошибки, ведь я ни разу не показывал свой код кому-либо, в школе только паскаль. Итак, следуя ненавидимому некоторыми чистому структурному подходу я написал первоначальный вариант на C++.
Задумка такова. Бот берет фразу из консоли, вычленяет слова, проверяет каждое по словарю, расположенному в файле «Memory.txt», и возвращает найденный ответ к каждому слову; если ни на одно слово ответ не найден, то он возвращает оговоренную фразу (не принципиально).
Словарь в файле «Memory.txt» структурирован простейшим образом:
слово=ответ
Пример:
яблоко=яблоки вкусные
Bot.h- заголовочный файл, о нем позже. Основные функции будут располагаться в файле Bot.cpp:
/**
Даниил Демидко, 2015
Основные функции Cbot
*/
#include"Bot.h"
Определим имя для словаря в этом же файле:
///Имя тектового файла- памяти бота
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();
}
В моем представлении структурный подход не отменяет инкапсуляцию, поэтому мы добавим анонимное пространство имен — для банальной инкапсуляции включающее в себя все предыдущие функции.
Таким образом предыдущие функции уже не будут доступны при подключении заголовочного файла «Bot.h». Позволю себе сослаться на гуру:
Это самый передовой способ избежать объявления функций и переменных со статическим связыванием.
Доступ может быть осуществлен только в рамках единицы
трансляции (т. е. в полученном после предварительной обработки файле),
в которой они находятся, точно так же, как к статическим переменным.
Стивен С. Дьюрхест, «C++. Священные знания»
Вот, все вместе:
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 *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» полностью:
/**
Даниил Демидко, 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»:
#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 — звучит невероятно оригинально, ведь правда?
#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)
Ruckus
07.09.2015 15:15Интересно, почему именно С++? И почему такая странная кодировка?
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; }
Ruckus
08.09.2015 09:18+2Это ужасно… Windows до сих пор не знает что такое UTF8, а
детипрограммисты пишут на C++ то, что стоило бы писать на скриптовом языке. Для обучения неплохо и, если вы школьник, даже круто, я таким в школе похвастаться не мог. Продолжайте в том же духе.
P.S. Но я бы автору рекомендовал посмотреть в сторону чего-нибудь попроще: Python, Ruby, Java. Ну или в сторону Qt если очень нравится C++. Вы получите как минимум кроссплатформенность (и никаких больше OEM 866) и гораздо более простую работу со строками (по крайней мере в Python). Но решать конечно вам.fareloz
08.09.2015 10:49+2А я бы не советовал. Миру также нужны люди, которые будут разбираться в извращениях. Ведь как раз они и пишут, например, Qt, который скрывает за своим интерфейсом все это. Поэтому пешать действительно автору.
ice2heart
10.09.2015 08:16+1Если честно с qt и виндовой консолью тоже не все так просто.
Я тоже за qt очень приятная штука.
Хотя если жить с плюсами без boost не обойтись порой.
Daniro_San
10.09.2015 10:21Qt как слишком огромен что бы привлекать его для такой простой задачи.
Да и где логика- использовать 2гигабитный фреймверк со своими велосипедами почти для всего
ради одного маленького консольного приложения?
Python мне не нравится из за синтаксиса (да да, вложенность) + это не компилируемый язык.
Java на десктопеужаснане слишком быстра и создавалась она уж точно не для такого.
Ruby пугает своим ООП + Ruby поднялся только благодаря рельсам, благодаря им же и упал.
Вот и остается (Заметьте, для такой задачи) чистый C++ без тотального ООП, обязательного для всех стиля,
промежуточной компиляции и всего прочего, присущего тому что вы перечислили.
Впрочем, каждому свое)Насчет строк не знаю, а как калькулятор Python вполне пригоден)fareloz
10.09.2015 10:33Забыли C#. Вполне подходит.
Daniro_San
10.09.2015 10:53Да, но промежуточная компиляция никуда не уходит. И можно забыть о UNIX
Ruckus
10.09.2015 10:58+1О *nix вы уже забыли с вашей кодировкой. Тот код, что вы воложили, будет нормально работать только на Windows.
Daniro_San
10.09.2015 11:43Насколько я знаю в *nix проблема с кодировками вообще не стоит так. Убираем setlocale и радуемся жизни.
Насчет Mono- много ли вы знаете людей постоянно использующих Mono с *nix?
Кроме разработчиков Mono конечно.
Всегда казалось, что C# — язык ориентированный на разработку в Windows.
Поэтому для наглядности решил использовать C++ при написании статьи)Ruckus
10.09.2015 11:56По поводу кодировки и *nix ничего не могу сказать, проблем быть не должно, но вероятно будут. Вообще в C и C++ лично для меня сложно что-то предсказывать про другие платформы, особенно в плане строк. Было дело пытались создать файл с русским именем и в него что-то на русском написать, с одной кодировкой в одном месте иероглифы, с другой в другом, как справились не помню, но решение искали втроём около часа. Так же делал Qt проект, под *nix'ами он собирался и работал как надо, а под Windows был Segmentation fault, очень долго пришлось всё отлаживать. С тех пор с C и C++ стараюсь не связываться, хоть иногда и приходится.
По поводу Mono я вообще ничего не знаю, как и о C#. Мне всегда, так же как и вам, казалось, что язык нацелен на Windows и кросыплатформенность у него посредственная, а у меня Windows только в виртуалке.
fareloz
10.09.2015 11:00+1Уже было тут две статьи по поводу «кто быстрее» — плюсы или шарп. ИМХО — у Вас программа настолько мала, что даже самый большой оверхед на C# не будет ощутим даже на самой плохенбкой системе. Кроме того для UNIX, если я не ошибься, Microsoft недавно начала поддерживать Mono.
Daniro_San
10.09.2015 10:54И кстати, GUI для бота я написал как раз на C#+WPF
fareloz
10.09.2015 10:58В ветке идет обсуждение «почему С++, ведь у него строки», а не про интерфейс. Поэтому и предложил все на шарпе.
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; } }
Ruckus
10.09.2015 10:56Qt не мал, но на нём хотябы писать можно, на чистых плюсах разве что серверные решения писать. Где вы там 2 гигабИта нашли я не знаю, вообще единицы измерения у вас не менее странные, чем кодировка, у меня выходило около 50-100МБ библиотек ссобой, что тоже не мало, но в современном мире особой роли не играют.
Синтаксис Python как говорится на вкус и цвет, но вот про компилируемость и «калькулятор» я совсем не понял. Зачем вам компилируемость? Что вы хотели сказать скрытым текстом? Ну да ладно, дело ваше. То что на этом языке ваша программа сократится раз в 10, а по скорости будет такая же плюс отладка в разы проще это впринципе не существенно. То что упаковать в .exe можно тоже, помимо того будет работать одинаково хорошо везде где есть интерпретатор питона. В общем много фич, но это не важно.
Java, на удивление, частично вытеснила C и C++ из серверного сегмента, а вы говорите, что она на десктопе медленная. Вы замеряли? Тесты просматривали хотябы? Почему не говорите, что скриптовые языки медленные, а про яву внезапно заявили? Знаете, но многие ресурсы вообще утверждают, что Java быстрей C++, вот, например.
Хм. Ну если вы боитесь ООП, то ок. Мне ООП кажется более удобным и перспективным.
А судя по вашим замечаниям создаётся ощущение, что вам не 16, а 61. Очень «устаревшие» взгляды, как по мне.ice2heart
10.09.2015 11:03Последний Qt если собирать MSVC в 10-12 мегабайт укладывается (без некоторых модулей).
Java щас больше в интерпрайзе, потому что покупной софт на джаве, а держать ещё и плюсовиков никому не хочется, вот и лезет в java. (зы по вашей ссылке я так и не увидел что java быстрее, а вот памяти точно сильно больше ест)Ruckus
10.09.2015 11:21Прошу прощения, читал, что быстрее несколько в другом месте, а этот ресурс обычно привожу для сравнения скорости ЯП и по ошибке интерпретировал приведённые там данные наоборот. И тем не менее там есть тест в котором Java отработала быстрее. По памяти я давно уже не заморачиваюсь, в современных ПК её с запасом, хотя возможно стоило бы.
А с Qt работал последний раз с 4.8-5.0 и с gcc/mingv. Если таскать ссобой заранее скомпилированные библиотеки, которые сам проект предоставляет у меня с GUI и network выходило около 80МБ. Сейчас возможно что-то поменялось, а по поводу MSVC вообще не знаю ибо работаю под Linux, дома Mac и так уже давно.Daniro_San
10.09.2015 11:49Знаете, вам то может быть и удобно таскать с собой скомпилированные библиотеки из Qt по отдельности- а мне нет. Не вижу в этом смысла. Ну а насчет 2гб- загляните но оф сайт Qt
Что то мне подсказывает, что я еще и занизил цифры.Ruckus
10.09.2015 12:08Не совсем понял о чём вы. Если брать *nix, то там библиотеки ставятся в системе и такскать их ссобой вообще не надо. Если брать Windows, то для разработки, возможно, нужны все, но таскать с приложением можно только те, которые это приложение использует. В случае данного приложения хватит всего одной(если я ничего не путаю). Смысл лишь в том, чтоб софт работал и писать код этого софта было удобней, приятней и быстрее.
По объёму на оф. сайте сходу не нашёл, раньше весили в полном комплекте меньше 1ГБ включая отладочные, которые весят раза в 2 больше и нужны только для отладки. Исходники библиотек вообще не нужны в большинстве случаев.Daniro_San
10.09.2015 12:20А как насчет этого?
Я бы не хотел разбираться и возиться с Qt ради пары библиотек.
А использование Qt без Qt Creator автоматически означает возню с настройкой IDE и компилятора под Qt.Ruckus
10.09.2015 12:36Возможно стоит ткнуть на стрелочки и посмотреть что он там ставит поподробней?
И последняя версия на данный момент 5.5, где вы взяли этот установщик?
P.S. И я конечно не исключаю, что что-то изменилось, но человек несколькими постами выше говорит об обратном.
Kop3t3
12.09.2015 07:01Опять-таки, столько весит среда разработки, необходимые для работы софта либы весят меньше. Но ИМХО, чистых сей хватит — если мы всё ещё обсуждаем кодировки, то легче нагуглить (или написать) простенький конвертер кодировок. (Хотя, некоторые считают, что тут пары строчек безо всяких либ хватит)
Ruckus
12.09.2015 11:58Гораздо приятней, чем 866, но всё ещё не кроссплатформенно (или я ошибаюсь?). А есть какой-нибудь вариант, чтобы во всех ОС работало нормально?
PS Я в институте всегда делалsetlocale(LC_ALL, "Russian");
Для вывода вроде помогало, но для ввода непомню.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
Daniro_San
10.09.2015 11:46Хорошо, где конкретно в этой задаче применимо ООП?
Можно было бы создать статический класс (привет C#), инкапсулировать функции немного по другому, и это все?
Daniro_San
10.09.2015 11:58Насчет отладки- как мне казалось современные IDE давно решили эту проблему…
И легкость (вами заявленная) в отладке- еще не повод выбирать Python
Daniro_San
10.09.2015 12:07Насчет Java.
Разумеется, я просматривал тесты.
Не видно, где это Java хоть немного на равных с C++.
Да и элементарное рассуждение- jvm с ее не далеко лучшей интеграцией с системой будет явно уступать C++ везде где только можно.
Кстати, в большинстве «сравнений C++ и Java» сравнения ведутся с примитивными типами, не с кучей, и к тому же Java оптимизирует код, компиляторы C++ в этом отношении ведут себя честнее.
Я не против jvm, я просто не понимаю вашего беспокойства за Java)Ruckus
10.09.2015 12:33Я не за Java. C++ тоже умеет делать и -O2 и -O3, так что в чём честность неясно.
Я просто не понимаю почему C++ и почему вы называете его единственным вариантом для данной задачи.
IDE не решают всех проблем, я уже ощутил это. Да и не все ими пользуются. На данный момент сравнивая все языки на которых я писал меньше всего мне нравятся C и C++ за черезмерную сложность, большое количество кода, сложную отладку (а пару лет назад мне так не казалось, но я просто не видел другого), необходимость прямой работы с памятью, очень много непредвиденных поведений (они конечно вытекают из документации, но запомнить и всегда держать в голове их все довольно сложно) и так далее. И как мне кажется все эти минусы оправданы только когда нам нужна очень высокая производительность, а в задаче, описанной в статье, сверхпроизводительности не требуется и разница между С++ и чем угодно другим будет незаметна.whunter
10.09.2015 15:01Я наоборот полюбил С за возможность прямой работы с памятью (да, рассматриваю как фичу), и навык разбираться в непредвиденных поведениях. При этом не отрицаю полезности и функциональности других ЯП, считаю что они применимы в зависимости от задачи, это инструмент. Но для школьника считаю написание приложений на С\++ крайне полезным, несмотря на целесообразность в конкретной задаче.
Daniro_San
10.09.2015 12:16Насчет:
Хм. Ну если вы боитесь ООП, то ок. Мне ООП кажется более удобным и перспективным
Буду благодарен, если вы покажете мне, как улучшить код в данном случае используя ООП.
Не нужно создавать систему с наследованием классов ради пары функций.
Если вам так нравится уделять кучу времени решению абстрактных задач, построению иерархии классов, по ходу чего частенько появляется еще с 10ок проблем только из за ООП- то пожалуйста.
Если _вам_так_нравится_Python_ опять же ваше _дело_.Ruckus
10.09.2015 12:51Я лишь отвечаю на ваши комментарии и не могу понять ваш выбор. Что мне нравится это отдельная тема. Я лишь привожу доводы почему что-то лучше/хуже для тех или иных задач и пытаюсь от вас добиться доводов почему другие языки не могут решить ту же задачу с меньшими трудозатратами.
Если бы вы написали, что обожаете C++, а остальные языки втопку, было бы понятней, но когда вы начинаете объяснять, что все остальные такие плохие, хочется донести до вас, что не всегда это так и не в этом случае точно, ну или хотябы для себя узнать чем же всётаки так хорош C++, возможно я его недооценивал.
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++ интерпретаторы скриптовых языков.
На чем то нужно учиться.
roman_kashitsyn
07.09.2015 15:36+2Неясно, зачем нужны внешние инклюд-гарды в Bot.h.
Более того, если кому-то нужны стримы, пусть заинклюдит их в соответствующем cpp-файле. В хедер-файле с одной функцией, не имеющей в сигнатуре стримов, им не место.
chuikoffru
07.09.2015 16:03Хм, спасибо за идею. Захотелось на Go сделать что-то подобное, только на узкую тему.
maaGames
07.09.2015 16:30+1> std::wcout<<«Bot: Не распознана ключевая последовательность!»
Уточка говорит: «Зря-зря».
По коду ничего говорить не буду, но дам пару рекомендаций для последующей реализации:
1. В юникод потоки следует посылать юникод строки. Но сперва нужно подключить к ним русскую локаль, тогда можно на человеческом языке писать, без 866 кодировки.
2. Перед сохранением конвертируй строки в utf8; при чтении — обратно в юникод.
3. Про ASCII строки пора бы уже начать забывать.
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
Daniro_San
08.09.2015 07:04Спасибо, насчет возврата константы вы пожалуй правы)
Старался использовать const везде кроме тех случаев где его нельзя использовать, следуя советам из комментариев прочитанных ранее на хабре)Hertz
08.09.2015 09:50+1const на user-defined типе, возвращаемом из функции, препятствует move-семантике, и никакой пользы не приносит. Поскольку может происходить копирование, этот const все равно ничего не дает. const (не top-level) имеет смысл при возврате из функции только если возвращается ссылочный тип (ссылка или указатель).
Daniro_San
08.09.2015 07:39И еще есть вопрос- почему extern здесь не нужен?
Он нужен только в при объявлении прототипа из .cpp?Hertz
08.09.2015 09:52+1Функции по-умолчанию имеют external linkage, именно чтобы избавиться от этого умолчания вы использовали anonymous namespace.
Hertz
Стоило использовать std::vector<std::string> вместо сырого владеющего указателя, не хочется вектор — тогда std::unique_ptr<std::string[]>.
Cтоит использовать std::string_type::npos, эта константа не обязана быть -1.
Использование const на возвращаемом по значению типе предотвращает всякие оптимизации.