Console based Vocabulary Trainer

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
7
down vote

favorite












I wrote a console based Vocabulary Trainer to practice C++.



Its possible to add and remove vocabulary and practice it with it.



I save and read the vocabulary from a file to be able to read it.



Also certain settings like target and source language color of the console get saved in a settings file.



I know currently it is not portable because I use Windows functions.



I tested it on a Mexican Windows 10 system and a German Windows 7 system.



I wonder if I'm handling the widestrings correctly. During the development I got into the problem that special signs like öäüß don't get displayed properly, so I switched to widestrings in the program. I wonder if my approach is right for this.



Also, I wonder if it's a good idea to make classes for the Menus.



In the next step I want to make this a GUI application to practice making GUIs, so I think it's a good time to review the code. Feel free to comment on any part you like.



File_Vocabulary.h



#ifndef FILE_VOCABULARY_GUARD100420181849
#define FILE_VOCABULARY_GUARD100420181849

#pragma once

#include <fstream>
#include <sstream>
#include <cstdio>
#include <fcntl.h>
#include <io.h>

#include "Vocabulary.h"

namespace voc

class File_Vocabulary
public:
File_Vocabulary(const std::string& in_filename);

void add(const Vocabulary& v);
bool erase(const std::wstring& s); // erase by word
bool erase(const int row); // erasse by row
bool is_match(const Vocabulary& v); // checks if vocabulary is exactly like this in file

int find(const std::vector<std::wstring> s); // checks if words are in file and returns row of it. 0 if no row
int find(std::wstring s); // checks if words are in file and returns row of it. 0 if no row

std::vector<Vocabulary> get_part(const int begin, const int end); // return in chuncks specify how many rows of Vocabulary you want
Vocabulary get_row(const int row); // return row
int size()const return count;

size_t max_size_source()const;
size_t max_size_target()const;

void reset_learned(); //resets learned to 0 in all vocabulary
private:
std::string filename;
int count; //count of vocabulary;
;


#endif


File_Vocabulary.cpp



#include "File_Vocabulary.h"

namespace voc

//____________________________Member functions

File_Vocabulary::File_Vocabulary(const std::string& in_filename)
:filenamein_filename,count0

_setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
_setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

std::wifstream ifs filename ;
if (!ifs) return; // file doesnt exist yet

for (Vocabulary curr; ifs >> curr;)
++count;



void File_Vocabulary::add(const Vocabulary& v)

const std::string out_filename = "tmp_" + filename;
//to close the files
std::wifstream ifs filename ;
if (ifs) // dont check if file doesnst exist yet

std::wofstream ofs out_filename ;
bool added = false;

for (Vocabulary curr; ifs >> curr;)
if (curr.source_words() == v.source_words() && !added) //replace definition of vocabulary
curr.set_target_words(v.target_words());
curr.set_practiced_right(v.practiced_right());
added = true;

else if (curr.source_words() > v.source_words() && !added)
ofs << v << 'n';
++count;
added = true;

ofs << curr << 'n';


if (!added) ofs << v << 'n'; // case file was empty


else //special case first word to add
std::wofstream ofs filename ;
ofs << v << 'n';
return;


std::remove(filename.c_str());
std::rename(out_filename.c_str(), filename.c_str());


bool File_Vocabulary::erase(const std::wstring& s) //search by word to erase

const std::string out_filename = "tmp_" + filename;
bool erased = false;

std::wifstream ifs filename ;
if (!ifs) return false;

std::wofstream ofs out_filename ;

for (Vocabulary curr; ifs >> curr;)
if ((curr.source_words() == extract_words(s)


if (std::remove(filename.c_str()) != 0)
throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
"file operation delete old vocabulary failedn");
if (std::rename(out_filename.c_str(), filename.c_str()) != 0)
throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
"file operation rename new to old failedn");

return erased;


bool File_Vocabulary::erase(const int row)


bool File_Vocabulary::is_match(const Vocabulary& v) // checks if vocabulary is exactly like this in file

std::wifstream ifs filename ;
if (!ifs) return false;

for (Vocabulary curr; ifs >> curr;)
if (v.source_words() == curr.source_words())
if (v.target_words() == curr.target_words())
return true;
else
return false;


throw std::runtime_error("bool File_Vocabulary::erase(const int row)nDatabase corrupt. Original word is not in database.n");
return false;


int File_Vocabulary::find(const std::vector<std::wstring> s) //checks if word is in file and returns row of it

std::wifstream ifs filename ;
if (!ifs) return 0;

int row = 0;
for (Vocabulary curr; ifs >> curr;)
if (curr.source_words() == s
return -1;


int File_Vocabulary::find(std::wstring s) //checks if word is in file and returns row of it

std::wistringstream ist s ;

std::vector<std::wstring> vs;
for (std::wstring in; ist >> in;)
vs.push_back(in);

return find(vs); // call the other find routine


std::vector<Vocabulary> File_Vocabulary::get_part(const int begin, const int end) //return in chuncks specify how many rows of Vocabulary you want


Vocabulary File_Vocabulary::get_row(const int row) // return row

std::vector<Vocabulary> ret = get_part(row, row);
return *ret.begin();


void File_Vocabulary::reset_learned()

std::wifstream ifs filename ;
if (!ifs) return;

const std::string out_filename = "tmp_" + filename;
std::wofstream ofs out_filename ;

for (Vocabulary curr; ifs >> curr;)
curr.set_practiced_right(0);
ofs << curr << 'n';


std::remove(filename.c_str());
std::rename(out_filename.c_str(), filename.c_str());


size_t File_Vocabulary::max_size_source() const

std::wifstream ifs filename ;
if (!ifs) return 0;

size_t ret = 0;
for (Vocabulary curr; ifs >> curr;)
if (curr.source_words().size() > ret)
ret = curr.source_words().size();

return ret;


size_t File_Vocabulary::max_size_target() const

std::wifstream ifs filename ;
if (!ifs) return 0;

size_t ret = 0;
for (Vocabulary curr; ifs >> curr;)
if (curr.target_words().size() > ret)
ret = curr.target_words().size();

return ret;




Menu.h



#ifndef MENU_GUARD110420181806
#define MENU_GUARD110420181806

#pragma once

#include <iostream>
#include <sstream>
#include <fcntl.h>
#include <io.h>

#include "Menu_settings.h"
#include "Menu_show.h"

namespace voc
class Menu
public:
Menu(const std::string& file_settings, const std::string& file_vocabulary);
private:
void init_settings();
void menu_main();
void menu_practice();
void menu_add();

File_Vocabulary file_voc;
Settings settings;
std::string fname_settings;
std::string fname_vocabulary;
;


#endif


Menu.cpp



#include"Menu.h"

namespace voc
//____________________________Member functions
Menu::Menu(const std::string& file_settings, const std::string& file_vocabulary)
:settings 10, L"spanish", L"german", 0, 15 ,
file_voc file_vocabulary ,
fname_settingsfile_settings,
fname_vocabularyfile_vocabulary

_setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
_setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

init_settings();
menu_main();


void Menu::init_settings()
// try to read settings from file
// if not take default values and write to new file
// change color from settings

if (!read_settings_from_file(fname_settings, settings))
write_settings_to_file(fname_settings, settings);

change_color(settings.color_background(), settings.color_font());


void Menu::menu_main()

enum class Menu_main_choice
practice = 1,
add = 2,
show = 3,
settings = 4,
exit = 5
;

while (true)
Menu_main_choice choice = static_cast<Menu_main_choice>(get_int(1, 5,
bissmann_print +
L"n Enter a number to choose an option:"
"n [1] Practice vocabulary"
"n [2] Add new vocabulary to database"
"n [3] Show all words currently in the database. Delete words in database"
"n [4] Settings"
"n [5] End program"
"n"));

switch (choice)
case Menu_main_choice::practice: menu_practice();break;
case Menu_main_choice::add: menu_add(); break;
case Menu_main_choice::show:
Menu_show ms fname_settings,settings ,file_voc ;
break;

case Menu_main_choice::settings:
Menu_settings ms fname_settings,settings ,file_voc ;
settings = ms.get_settings();
break;

case Menu_main_choice::exit: return;




void Menu::menu_practice()

clear_screen();
int count_of_rows = file_voc.size()-1;
if (count_of_rows == 0) // file empty fill first with vocabulary
std::wcout << "n Error: Database is empty. Fill database first with vocabularyn";
utility::keep_window_open(L"q");
return;


int amount = get_int(1, 1000,bissmann_print +L"n Enter how many words you want to practice.Range 1 - 1000 n");

Vocabulary rnd_voc; // vocabulary
int correct_words = 0;
for (int i = 1; i <= amount; ++i)
clear_screen();
std::wcout <<bissmann_print;

int repeat_rnd = 0;

do
rnd_voc = file_voc.get_row(utility::get_random(1, count_of_rows));
++repeat_rnd;
if (repeat_rnd == 1000) //assuming after 1000 rolls no valid word could be found so all are "learned"
std::wcout << "n You learned all vocabulary by repeating each word " << settings.threshold_learned() << " times correct n";
utility::keep_window_open(L"q");
return;

while (rnd_voc.practiced_right() >= settings.threshold_learned());

clear_screen();
std::wcout << bissmann_print
<< "n Word nummber " << i << " out of " << amount << ":n"
<< "n The " << settings.name_of_source_language() << " word is:nn " << rnd_voc.source() << "n"
<< "n Enter the translation in " << settings.name_of_target_language() << ":nn";

std::wcin.ignore();
std::vector<std::wstring> input_words = get_words();

if (input_words == rnd_voc.target_words()) //word translated right
++correct_words;

int practiced_right = rnd_voc.practiced_right();
++practiced_right;
rnd_voc.set_practiced_right(practiced_right);

std::wcout << "n That is correct!!!n";

if (rnd_voc.practiced_right() >= settings.threshold_learned())
std::wcout << "n You learned the word. You answered it correct " << rnd_voc.practiced_right() << " timesn";

else //translated wrong
rnd_voc.set_practiced_right(0);
std::wcout << "n That is wrong !!!"
<< "n The correct translation is: " << rnd_voc.target()<<'n';

utility::keep_window_open(L"q");
file_voc.add(rnd_voc);

clear_screen();
std::wcout << bissmann_print
<< "n You translatet " << correct_words << " out of " << amount << " words correctly to " << settings.name_of_target_language()<<'n';
utility::keep_window_open(L"q");
return;


void Menu::menu_add()

while (true)
clear_screen();
int choice = get_int(1, 2,
bissmann_print +
L"n [1] Add a new word to the database"
"n [2] Return to main menue"
"n");
if (choice == 2) return;

clear_screen();
std::wcout << bissmann_print << "n Enter the " << settings.name_of_source_language() << " wordn";

Vocabulary add_voc;
std::wcin.ignore();
std::vector<std::wstring> org_words = get_words();
add_voc.set_source_words(org_words);

clear_screen();
std::wcout << bissmann_print << "n Enter the translation for '" << add_voc.source() << "' in " << settings.name_of_target_language() << 'n';

std::vector<std::wstring> tar_words = get_words();
add_voc.set_target_words(tar_words);

file_voc.add(add_voc);






Menu_settings.h



#ifndef MENU_SETTINGS_GUARD160420182054
#define MENU_SETTINGS_GUARD160420182054

#pragma once

#include <string>
#include <iostream>
#include <sstream>
#include <fcntl.h>
#include <io.h>

#include "Settings.h"
#include "misc.h"
#include "File_Vocabulary.h"

namespace voc
class Menu_settings
public:
Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
Settings get_settings() const return settings;
private:
void menu_change_color();
void menu_set_learned();
void menu_reset_learned();
void menu_set_languages();

std::string filename_settings;
Settings settings;
File_Vocabulary file_voc;
;



#endif


Menu_settings.cpp



#include "Menu_settings.h"

namespace voc

Menu_settings::Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
:filename_settingsfile_settings,settingsset, file_vocfv

_setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
_setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

enum class Menu_settings_choice
change_color = 1,
set_learned = 2,
reset_learned = 3,
set_languges = 4,
exit = 5
;

while (true)
Menu_settings_choice choice = static_cast<Menu_settings_choice>(get_int(1, 5,
bissmann_print +
L"n Enter a number to choose an option:"
"n [1] Change color of the background or the letters"
"n [2] Define how often to repeat a word until it counts as learned"
"n [3] Set all vocabulary to not learned"
"n [4] Define target and source language"
"n [5] Return to main menue"
"n"));

switch (choice)
case Menu_settings_choice::change_color: menu_change_color(); break;
case Menu_settings_choice::set_learned: menu_set_learned(); break;
case Menu_settings_choice::reset_learned: menu_reset_learned(); break;
case Menu_settings_choice::set_languges: menu_set_languages(); break;
case Menu_settings_choice::exit: return;




void Menu_settings::menu_change_color()


void Menu_settings::menu_set_learned()

std::wostringstream ost;
ost << "n Currently each vocabulary has to be translated right"
"n " << settings.threshold_learned() << " times in a row to make it count as learned."
"n Enter a new value for practiced right. Range: 1 - 99n";

settings.set_threshold_learned(get_int(1, 99, bissmann_print + ost.str()));
write_settings_to_file(filename_settings, settings);


void Menu_settings::menu_reset_learned()

int choice = get_int(1, 2,
bissmann_print +
L"n Choose an option:"
"n [1] Reset all words to not learned. Practice right = 0"
"n [2] Return to settingsn");

if (choice == 2) return;

choice = get_int(1, 2,
bissmann_print +
L"n Are you SUPER SUPER SURE??"
"n [1] Reset all words to not learned. Practice right = 0"
"n [2] Return to settingsn");

if (choice == 2) return;

file_voc.reset_learned();
std::wcout<<"n All words changed to not learnedn";
utility::keep_window_open(L"q");


void Menu_settings::menu_set_languages()

enum class Menu_change_language_choice
change_source_language = 1,
change_target_language = 2,
return_to_settings = 3
;

std::wostringstream ost;
ost<< "n Currently all words need to be translated from "<<settings.name_of_source_language()<<" to "<< settings.name_of_target_language()<<
"n Choose an option to change the language"
"n [1] Change the source language ("<< settings.name_of_source_language() <<")"
"n [2] Change the target language ("<< settings.name_of_target_language()<<")"
"n [3] Return to settingsn";

Menu_change_language_choice choice = static_cast<Menu_change_language_choice>(get_int(1, 3,bissmann_print + ost.str()));

if (choice == Menu_change_language_choice::return_to_settings) return;
std::cin.ignore();

switch (choice)
case Menu_change_language_choice::change_source_language:
std::wcout << "Enter the new source language:n";
std::wstring in;
getline(std::wcin,in);

if (in.empty())
std::wcerr << "Invalid empty string enteredn";
utility::keep_window_open(L"q");
return;


settings.set_name_of_source_language(in);
write_settings_to_file(filename_settings, settings);
std::wcout<<"n New source language saved to settings";
utility::keep_window_open(L"q");
break;

case Menu_change_language_choice::change_target_language:
std::wcout << "Enter the new target language:n";
std::wstring in;
getline(std::wcin, in);

if (in.empty())
std::wcerr << "Invalid empty string enteredn";
utility::keep_window_open(L"q");
return;


settings.set_name_of_target_language(in);
write_settings_to_file(filename_settings, settings);
std::cout<<"n New target language saved to settings";
utility::keep_window_open(L"q");
break;

case Menu_change_language_choice::return_to_settings:
return;





Menu_show.h



#ifndef MENU_SHOW_GUARD170420182020
#define MENU_SHOW_GUARD170420182020

#pragma once

#include <string>
#include <iostream>
#include <sstream>
#include <fcntl.h>
#include <io.h>
#include <iomanip>

#include "Settings.h"
#include "misc.h"
#include "File_Vocabulary.h"

namespace voc
class Menu_show
public:
Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
private:
enum class Menu_show_choice
start,
search,
erase,
exit
;

void menu_search();
void menu_erase();

std::string filename_settings;
Settings settings;
File_Vocabulary file_voc;
Menu_show_choice step;
int delete_row;

static constexpr int rows_per_page = 15;
static constexpr char left_arrow = 75;
static constexpr char right_arrow = 77;

static constexpr int max_len_of_display = 35; // complete window = 80
;



#endif


Menu_show.cpp



#include "Menu_show.h"

namespace voc

Menu_show::Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
:filename_settings file_settings , settings set , file_voc fv ,stepMenu_show_choice::start,delete_row0

_setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
_setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

int page = 1;
while (true)
if (file_voc.size() < 1)
clear_screen();
std::wcout << "n Database is emptyn";
utility::keep_window_open(L"q");
return;



int count_of_pages = file_voc.size() / rows_per_page;
if ((file_voc.size() % rows_per_page) != 0) //if fracture there must be one more page
++count_of_pages;

clear_screen();
std::wcout << "row ";
std::wcout << settings.name_of_source_language() << ':';

// check what is possible max len and then break to the next line
for (size_t i = 0; i < max_len_of_display - settings.name_of_source_language().size(); ++i) //max len = longest word in database !!!
std::wcout << ' ';

std::wcout << settings.name_of_target_language() << ':';

for (size_t i = 0; i < max_len_of_display - settings.name_of_target_language().size(); ++i) //max len = longest word in database !!!
std::wcout << ' ';

std::wcout << "OK ";

for (int i = 0; i < 80; ++i)
std::wcout << '-';

int start_row = 0 + (page - 1) * rows_per_page;
int end_row = 0;
if (page == count_of_pages)
end_row = file_voc.size()-1;
else
end_row = start_row + rows_per_page - 1;

//read vocabulary from file in chunks
std::vector<Vocabulary> print_voc file_voc.get_part(start_row, end_row) ;

for (size_t i=0; i<print_voc.size(); ++i)
std::wcout << std::setw(4) << std::left << start_row+1 + i
<< "
for (int i = 0; i < 80; ++i)
std::wcout << '-';

if (page != 1) std::wcout << "<-";
else std::wcout << " ";
if (page != count_of_pages) std::wcout << " ->";
else std::wcout << " ";
std::wcout << " page " << page << " of " << count_of_pages;

switch (step)
case Menu_show_choice::exit:
return;
case Menu_show_choice::start:
std::wcout <<
"n Choose an option:"
"n [1] Search for a word to delete it"
"n [2] Return to main menue"
"n";
int choice = read_single_char();
if (choice == '1')
step = Menu_show_choice::search;
else if (choice == '2') // Back to main Menue
return;
else
choice = read_single_char(); //Call twice because first time return always 0xE0 second returns code
step = Menu_show_choice::start;

if (choice == left_arrow && page != 1)
--page;
if (choice == right_arrow && page != count_of_pages)
++page;
break;

case Menu_show_choice::search: menu_search();
break;
case Menu_show_choice::erase: menu_erase();
break;




void Menu_show::menu_search() //ugly better with a class

std::wcout << "n Enter a word or the specific row to searchn a entry in the vocabulary database:n";
std::wstring input;
std::wcin.ignore();
std::getline(std::wcin, input);

bool is_number = true;

try
delete_row = std::stoi(input);
--delete_row;//because humans start on 1 not on 0

catch (...) // catch stoi std::invalid_argument std::out_of_range
is_number = false;


if (!is_number) //search by name
delete_row = file_voc.find(input); // ??? delete row == 0

if (delete_row == 0) //No match
std::wcout << "n No match found in filen";
utility::keep_window_open(L"q");
step = Menu_show_choice::start; // break and set global menu
return;


else // search by row number
if (delete_row < 0
step = Menu_show_choice::erase; // go to next step erase


void Menu_show::menu_erase()

Vocabulary delete_voc;
delete_voc = file_voc.get_row(delete_row); // get word to delte for display

int choice = 0;
bool repeat = false;

for (int i = 0; i <2; ++i) "
<< std::setw(max_len_of_display) << std::left << delete_voc.target().substr(0, max_len_of_display)
<< "
file_voc.erase(delete_row);
std::wcout << "n Hasta la vista baby!!!!!"
<< "n Entry has been terminated.n";
utility::keep_window_open(L"q");

step = Menu_show_choice::start;




misc.h



#ifndef MISC_GUARD120420181946
#define MISC_GUARD120420181946

#pragma once

#include <string>
#include <sstream>
#include <map>
#include <cctype>
#include <cwctype>
#include <iostream>
#include <conio.h>
#include <Windows.h>

#include "Utility.h"

namespace voc

const std::wstring bissmann_print =

L"n--------Bißmann's-------"
"n---Vocabulary Trainer---"
"n---------V3.0-----------"
"n"
;

void clear_screen(); //Windows only not portable
int read_single_char(void);
void change_color(char background, char font); //Windows only not portable
std::wstring hex_to_color(int hex);
char hex_to_ascii(char c);

// functions to safely read in an integer
void skip_to_int();
int get_int();
int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry = L"");

std::vector<std::wstring> get_words();


#endif


misc.cpp



#include "misc.h"

namespace voc

void clear_screen()

system("cls"); //WINDOWS ONLY


int read_single_char(void)

return _getch(); //WINDOWS ONLY


void change_color(char background, char font)

std::string color = "COLOR ";
color.push_back(hex_to_ascii(background));
color.push_back(hex_to_ascii(font));
system(color.c_str()); //WINDOWS ONLY


std::wstring hex_to_color(int hex)

const std::vector<std::wstring> color_hex =

L"Black",
L"Blue",
L"Green",
L"Aqua",
L"Red",
L"Purple",
L"Yellow",
L"White",
L"Gray",
L"Light Blue",
L"Light Green",
L"Light Aqua",
L"Light Red",
L"Light Purple",
L"Light Yellow",
L"Bright White"
;

return color_hex[hex];


char hex_to_ascii(char c)

if (c < 10) // convert number from hex to ascii. different offsets for 1-9 and a-z in ascii file
c += 48;
else
c += 55;
return c;


void skip_to_int()

if (std::wcin.fail()) // we found sth that wasnt an integer
std::wcin.clear(); // wed like to look at the characters
for (wchar_t ch; std::wcin >> ch;) ch == '-')
std::wcin.unget(); //put the digit back so that we can read the number
return;






int get_int()

int n = 0;
while (true)
if (std::wcin >> n) return n;
skip_to_int();



int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry)

while (true)
clear_screen();
std::wcout << greeting;

int n = get_int();
if (low <= n && n <= high) return n;
if (!sorry.empty())
std::wcout << sorry;
utility::keep_window_open();




std::vector<std::wstring> get_words()
//reads in line and returns the individual words of it

std::wstring line;
std::getline(std::wcin, line);

std::wistringstream iss line ;
std::vector<std::wstring> ret;

for (std::wstring in; iss >> in;)
ret.push_back(in);
return ret;




settings.h



#ifndef SETTINGS_GUARD110420181833
#define SETTINGS_GUARD110420181833

#pragma once

#include <string>
#include <fstream>
#include <fcntl.h>
#include <io.h>

namespace voc

class Settings
public:
Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font);

void set_threshold_learned(int threshold) thr_learnd = threshold;
void set_name_of_source_language(const std::wstring& source_name) s_language = source_name;
void set_name_of_target_language(const std::wstring& target_name) t_language = target_name;
void set_color_background(int back) clr_back = back;
void set_color_font(int font) clr_font = font;

int threshold_learned() const return thr_learnd; ;
std::wstring name_of_source_language() const return s_language;
std::wstring name_of_target_language() const return t_language;
char color_background() const return clr_back;
char color_font() const return clr_font;
private:
int thr_learnd; //learned
std::wstring s_language;
std::wstring t_language;
char clr_back;
char clr_font;
;

std::wostream& operator<<(std::wostream& os, const Settings& obj);
std::wistream& operator>>(std::wistream& is, Settings& obj);

void write_settings_to_file(const std::string& filename, Settings& data);
bool read_settings_from_file(const std::string& filename, Settings& data);


#endif


Settings.cpp



#include "Settings.h"

namespace voc
Settings::Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font)
:thr_learnd threshold ,
s_language source_name ,
t_language target_name ,
clr_back color_back ,
clr_font color_font
color_back > 15

//____________________________Helper functions
std::wostream& operator<<(std::wostream& os, const Settings& obj)


os << obj.threshold_learned() << 't'
<< obj.name_of_source_language() << 't'
<< obj.name_of_target_language() << 't'
<< static_cast<int>(obj.color_background()) << 't'
<< static_cast<int>(obj.color_font());

return os;

std::wistream& operator>>(std::wistream& is, Settings& obj)

int learned;
std::wstring source;
std::wstring target;
int back;
int font;

is >> learned >> source >> target >> back >> font;

Settings ret(learned, source, target, static_cast<char>(back), static_cast<char>(font));
obj = ret;

return is;


void write_settings_to_file(const std::string& filename, Settings& data)

std::wofstream ofs filename ;
ofs << data;



bool read_settings_from_file(const std::string& filename, Settings& data)

std::wifstream ifs filename ;
if (!ifs) return false;

ifs >> data;
return true;




Utility.h



#ifndef UTILITY_GUARD11042018
#define UTILITY_GUARD11042018

#pragma once

#include <iostream>
#include <string>
#include <random>
#include <chrono>

namespace utility

inline void keep_window_open(std::wstring s)

if (s == L"") return;
std::wcin.clear();
std::wcin.ignore(120, 'n');
for (;;)
std::wcout << "Please enter " << s << " to exitn";
std::wstring ss;
while (std::wcin >> ss && ss != s)
std::wcout << "Please enter " << s << " to exitn";
return;



inline void keep_window_open()

std::wcin.clear();
std::wcout << "Please enter a character to exitn";
wchar_t ch;
std::wcin >> ch;
return;


int get_random(int min, int max);


#endif


Utility.cpp



#include "Utility.h"


namespace utility

int get_random(int min, int max)

std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(min, max);
return distribution(mt);




Vocabulary.h



#ifndef VOCABULARY_GUARD100420181927
#define VOCABULARY_GUARD100420181927

#pragma once

#include <string>
#include <vector>
#include <sstream>

namespace voc

class Vocabulary
public:
Vocabulary();
Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar);
Vocabulary(const std::wstring& org, const std::wstring& tar);

std::vector<std::wstring> source_words()const return src_words;
std::vector<std::wstring> target_words()const return tar_words;

std::wstring source()const;
std::wstring target()const;

int practiced_right() const return pr;

void set_source_words(const std::vector<std::wstring>& org) src_words = org;
void set_target_words(const std::vector<std::wstring>& tar) tar_words = tar;
void set_practiced_right(int p) pr = p;
private:
std::vector<std::wstring> src_words;
std::vector<std::wstring> tar_words;
int pr; //practiced_right
;

std::wostream& operator<<(std::wostream& os, const Vocabulary& obj);
std::wistream& operator>>(std::wistream& is, Vocabulary& obj);
inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs);

std::vector<std::wstring> extract_words(const std::wstring& line);
std::wstring words_to_line(const std::vector<std::wstring>& sv);


#endif


Vocabulary.cpp



#include "Vocabulary.h"

namespace voc
//____________________________Member functions
Vocabulary::Vocabulary()
:pr0



Vocabulary::Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar)
:src_wordsorg,tar_wordstar,pr0



Vocabulary::Vocabulary(const std::wstring& org, const std::wstring& tar)
: src_words extract_words(org) ,tar_words extract_words(tar) ,pr0



std::wstring Vocabulary::source()const return words_to_line(src_words);
std::wstring Vocabulary::target()const return words_to_line(tar_words);

//____________________________Helper functions
std::wostream& operator<<(std::wostream& os, const Vocabulary& obj)

for (const auto& x : obj.source_words())
os << x + L" ";
os << "# ";
for (const auto& x : obj.target_words())
os << x + L" ";
os << "# ";
os << obj.practiced_right();

return os;

std::wistream& operator>>(std::wistream& is, Vocabulary& obj)

std::wstring line;
std::getline(is,line);
std::wistringstream ifs line ;

std::vector<std::wstring> org;
bool first = false;
for (std::wstring s; ifs >> s;)
if (s == L"#")
first = true;
break;

org.push_back(s);


if (!first) // end of stream but first # not found
is.setstate(std::ios::failbit);
return is;


std::vector<std::wstring> tar;
bool second = false;
for (std::wstring s; ifs >> s;)
if (s == L"#")
second = true;
break;

tar.push_back(s);


if (!second) // same for second
is.setstate(std::ios::failbit);
return is;


std::wstring s;
ifs >> s;
obj.set_source_words(org);
obj.set_target_words(tar);
obj.set_practiced_right(std::stoi(s));

return is;


inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs)
// no check of valid member

return (lhs.source_words() == rhs.target_words()) && (lhs.target_words() == rhs.target_words());


std::vector<std::wstring> extract_words(const std::wstring& line)

std::wistringstream ifs line ;
std::vector<std::wstring> ret;
for (std::wstring s; ifs >> s;)
ret.push_back(s);
return ret;;


std::wstring words_to_line(const std::vector<std::wstring>& sv)
//makes a vector of strings to a line seperated by whitespace

std::wstring ret;

for (const auto&x : sv)
ret = ret + x + L' ';
if (!ret.empty()) // remove last ' '
ret.pop_back();
return ret;




main.cpp



#include <iostream>
#include <string>

#include "Menu.h"


const std::string filename_settings = "settings.txt";
const std::string filename_vocabulary = "vocsafe.txt";

int main()
try
voc::Menu menu(filename_settings, filename_vocabulary);

catch (std::runtime_error& e)
std::wcerr << e.what() << "n";
utility::keep_window_open(L"~");

catch (...)
std::wcerr << "unknown error " << "n";
utility::keep_window_open(L"~");







share|improve this question



























    up vote
    7
    down vote

    favorite












    I wrote a console based Vocabulary Trainer to practice C++.



    Its possible to add and remove vocabulary and practice it with it.



    I save and read the vocabulary from a file to be able to read it.



    Also certain settings like target and source language color of the console get saved in a settings file.



    I know currently it is not portable because I use Windows functions.



    I tested it on a Mexican Windows 10 system and a German Windows 7 system.



    I wonder if I'm handling the widestrings correctly. During the development I got into the problem that special signs like öäüß don't get displayed properly, so I switched to widestrings in the program. I wonder if my approach is right for this.



    Also, I wonder if it's a good idea to make classes for the Menus.



    In the next step I want to make this a GUI application to practice making GUIs, so I think it's a good time to review the code. Feel free to comment on any part you like.



    File_Vocabulary.h



    #ifndef FILE_VOCABULARY_GUARD100420181849
    #define FILE_VOCABULARY_GUARD100420181849

    #pragma once

    #include <fstream>
    #include <sstream>
    #include <cstdio>
    #include <fcntl.h>
    #include <io.h>

    #include "Vocabulary.h"

    namespace voc

    class File_Vocabulary
    public:
    File_Vocabulary(const std::string& in_filename);

    void add(const Vocabulary& v);
    bool erase(const std::wstring& s); // erase by word
    bool erase(const int row); // erasse by row
    bool is_match(const Vocabulary& v); // checks if vocabulary is exactly like this in file

    int find(const std::vector<std::wstring> s); // checks if words are in file and returns row of it. 0 if no row
    int find(std::wstring s); // checks if words are in file and returns row of it. 0 if no row

    std::vector<Vocabulary> get_part(const int begin, const int end); // return in chuncks specify how many rows of Vocabulary you want
    Vocabulary get_row(const int row); // return row
    int size()const return count;

    size_t max_size_source()const;
    size_t max_size_target()const;

    void reset_learned(); //resets learned to 0 in all vocabulary
    private:
    std::string filename;
    int count; //count of vocabulary;
    ;


    #endif


    File_Vocabulary.cpp



    #include "File_Vocabulary.h"

    namespace voc

    //____________________________Member functions

    File_Vocabulary::File_Vocabulary(const std::string& in_filename)
    :filenamein_filename,count0

    _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
    _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

    std::wifstream ifs filename ;
    if (!ifs) return; // file doesnt exist yet

    for (Vocabulary curr; ifs >> curr;)
    ++count;



    void File_Vocabulary::add(const Vocabulary& v)

    const std::string out_filename = "tmp_" + filename;
    //to close the files
    std::wifstream ifs filename ;
    if (ifs) // dont check if file doesnst exist yet

    std::wofstream ofs out_filename ;
    bool added = false;

    for (Vocabulary curr; ifs >> curr;)
    if (curr.source_words() == v.source_words() && !added) //replace definition of vocabulary
    curr.set_target_words(v.target_words());
    curr.set_practiced_right(v.practiced_right());
    added = true;

    else if (curr.source_words() > v.source_words() && !added)
    ofs << v << 'n';
    ++count;
    added = true;

    ofs << curr << 'n';


    if (!added) ofs << v << 'n'; // case file was empty


    else //special case first word to add
    std::wofstream ofs filename ;
    ofs << v << 'n';
    return;


    std::remove(filename.c_str());
    std::rename(out_filename.c_str(), filename.c_str());


    bool File_Vocabulary::erase(const std::wstring& s) //search by word to erase

    const std::string out_filename = "tmp_" + filename;
    bool erased = false;

    std::wifstream ifs filename ;
    if (!ifs) return false;

    std::wofstream ofs out_filename ;

    for (Vocabulary curr; ifs >> curr;)
    if ((curr.source_words() == extract_words(s)


    if (std::remove(filename.c_str()) != 0)
    throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
    "file operation delete old vocabulary failedn");
    if (std::rename(out_filename.c_str(), filename.c_str()) != 0)
    throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
    "file operation rename new to old failedn");

    return erased;


    bool File_Vocabulary::erase(const int row)


    bool File_Vocabulary::is_match(const Vocabulary& v) // checks if vocabulary is exactly like this in file

    std::wifstream ifs filename ;
    if (!ifs) return false;

    for (Vocabulary curr; ifs >> curr;)
    if (v.source_words() == curr.source_words())
    if (v.target_words() == curr.target_words())
    return true;
    else
    return false;


    throw std::runtime_error("bool File_Vocabulary::erase(const int row)nDatabase corrupt. Original word is not in database.n");
    return false;


    int File_Vocabulary::find(const std::vector<std::wstring> s) //checks if word is in file and returns row of it

    std::wifstream ifs filename ;
    if (!ifs) return 0;

    int row = 0;
    for (Vocabulary curr; ifs >> curr;)
    if (curr.source_words() == s
    return -1;


    int File_Vocabulary::find(std::wstring s) //checks if word is in file and returns row of it

    std::wistringstream ist s ;

    std::vector<std::wstring> vs;
    for (std::wstring in; ist >> in;)
    vs.push_back(in);

    return find(vs); // call the other find routine


    std::vector<Vocabulary> File_Vocabulary::get_part(const int begin, const int end) //return in chuncks specify how many rows of Vocabulary you want


    Vocabulary File_Vocabulary::get_row(const int row) // return row

    std::vector<Vocabulary> ret = get_part(row, row);
    return *ret.begin();


    void File_Vocabulary::reset_learned()

    std::wifstream ifs filename ;
    if (!ifs) return;

    const std::string out_filename = "tmp_" + filename;
    std::wofstream ofs out_filename ;

    for (Vocabulary curr; ifs >> curr;)
    curr.set_practiced_right(0);
    ofs << curr << 'n';


    std::remove(filename.c_str());
    std::rename(out_filename.c_str(), filename.c_str());


    size_t File_Vocabulary::max_size_source() const

    std::wifstream ifs filename ;
    if (!ifs) return 0;

    size_t ret = 0;
    for (Vocabulary curr; ifs >> curr;)
    if (curr.source_words().size() > ret)
    ret = curr.source_words().size();

    return ret;


    size_t File_Vocabulary::max_size_target() const

    std::wifstream ifs filename ;
    if (!ifs) return 0;

    size_t ret = 0;
    for (Vocabulary curr; ifs >> curr;)
    if (curr.target_words().size() > ret)
    ret = curr.target_words().size();

    return ret;




    Menu.h



    #ifndef MENU_GUARD110420181806
    #define MENU_GUARD110420181806

    #pragma once

    #include <iostream>
    #include <sstream>
    #include <fcntl.h>
    #include <io.h>

    #include "Menu_settings.h"
    #include "Menu_show.h"

    namespace voc
    class Menu
    public:
    Menu(const std::string& file_settings, const std::string& file_vocabulary);
    private:
    void init_settings();
    void menu_main();
    void menu_practice();
    void menu_add();

    File_Vocabulary file_voc;
    Settings settings;
    std::string fname_settings;
    std::string fname_vocabulary;
    ;


    #endif


    Menu.cpp



    #include"Menu.h"

    namespace voc
    //____________________________Member functions
    Menu::Menu(const std::string& file_settings, const std::string& file_vocabulary)
    :settings 10, L"spanish", L"german", 0, 15 ,
    file_voc file_vocabulary ,
    fname_settingsfile_settings,
    fname_vocabularyfile_vocabulary

    _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
    _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

    init_settings();
    menu_main();


    void Menu::init_settings()
    // try to read settings from file
    // if not take default values and write to new file
    // change color from settings

    if (!read_settings_from_file(fname_settings, settings))
    write_settings_to_file(fname_settings, settings);

    change_color(settings.color_background(), settings.color_font());


    void Menu::menu_main()

    enum class Menu_main_choice
    practice = 1,
    add = 2,
    show = 3,
    settings = 4,
    exit = 5
    ;

    while (true)
    Menu_main_choice choice = static_cast<Menu_main_choice>(get_int(1, 5,
    bissmann_print +
    L"n Enter a number to choose an option:"
    "n [1] Practice vocabulary"
    "n [2] Add new vocabulary to database"
    "n [3] Show all words currently in the database. Delete words in database"
    "n [4] Settings"
    "n [5] End program"
    "n"));

    switch (choice)
    case Menu_main_choice::practice: menu_practice();break;
    case Menu_main_choice::add: menu_add(); break;
    case Menu_main_choice::show:
    Menu_show ms fname_settings,settings ,file_voc ;
    break;

    case Menu_main_choice::settings:
    Menu_settings ms fname_settings,settings ,file_voc ;
    settings = ms.get_settings();
    break;

    case Menu_main_choice::exit: return;




    void Menu::menu_practice()

    clear_screen();
    int count_of_rows = file_voc.size()-1;
    if (count_of_rows == 0) // file empty fill first with vocabulary
    std::wcout << "n Error: Database is empty. Fill database first with vocabularyn";
    utility::keep_window_open(L"q");
    return;


    int amount = get_int(1, 1000,bissmann_print +L"n Enter how many words you want to practice.Range 1 - 1000 n");

    Vocabulary rnd_voc; // vocabulary
    int correct_words = 0;
    for (int i = 1; i <= amount; ++i)
    clear_screen();
    std::wcout <<bissmann_print;

    int repeat_rnd = 0;

    do
    rnd_voc = file_voc.get_row(utility::get_random(1, count_of_rows));
    ++repeat_rnd;
    if (repeat_rnd == 1000) //assuming after 1000 rolls no valid word could be found so all are "learned"
    std::wcout << "n You learned all vocabulary by repeating each word " << settings.threshold_learned() << " times correct n";
    utility::keep_window_open(L"q");
    return;

    while (rnd_voc.practiced_right() >= settings.threshold_learned());

    clear_screen();
    std::wcout << bissmann_print
    << "n Word nummber " << i << " out of " << amount << ":n"
    << "n The " << settings.name_of_source_language() << " word is:nn " << rnd_voc.source() << "n"
    << "n Enter the translation in " << settings.name_of_target_language() << ":nn";

    std::wcin.ignore();
    std::vector<std::wstring> input_words = get_words();

    if (input_words == rnd_voc.target_words()) //word translated right
    ++correct_words;

    int practiced_right = rnd_voc.practiced_right();
    ++practiced_right;
    rnd_voc.set_practiced_right(practiced_right);

    std::wcout << "n That is correct!!!n";

    if (rnd_voc.practiced_right() >= settings.threshold_learned())
    std::wcout << "n You learned the word. You answered it correct " << rnd_voc.practiced_right() << " timesn";

    else //translated wrong
    rnd_voc.set_practiced_right(0);
    std::wcout << "n That is wrong !!!"
    << "n The correct translation is: " << rnd_voc.target()<<'n';

    utility::keep_window_open(L"q");
    file_voc.add(rnd_voc);

    clear_screen();
    std::wcout << bissmann_print
    << "n You translatet " << correct_words << " out of " << amount << " words correctly to " << settings.name_of_target_language()<<'n';
    utility::keep_window_open(L"q");
    return;


    void Menu::menu_add()

    while (true)
    clear_screen();
    int choice = get_int(1, 2,
    bissmann_print +
    L"n [1] Add a new word to the database"
    "n [2] Return to main menue"
    "n");
    if (choice == 2) return;

    clear_screen();
    std::wcout << bissmann_print << "n Enter the " << settings.name_of_source_language() << " wordn";

    Vocabulary add_voc;
    std::wcin.ignore();
    std::vector<std::wstring> org_words = get_words();
    add_voc.set_source_words(org_words);

    clear_screen();
    std::wcout << bissmann_print << "n Enter the translation for '" << add_voc.source() << "' in " << settings.name_of_target_language() << 'n';

    std::vector<std::wstring> tar_words = get_words();
    add_voc.set_target_words(tar_words);

    file_voc.add(add_voc);






    Menu_settings.h



    #ifndef MENU_SETTINGS_GUARD160420182054
    #define MENU_SETTINGS_GUARD160420182054

    #pragma once

    #include <string>
    #include <iostream>
    #include <sstream>
    #include <fcntl.h>
    #include <io.h>

    #include "Settings.h"
    #include "misc.h"
    #include "File_Vocabulary.h"

    namespace voc
    class Menu_settings
    public:
    Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
    Settings get_settings() const return settings;
    private:
    void menu_change_color();
    void menu_set_learned();
    void menu_reset_learned();
    void menu_set_languages();

    std::string filename_settings;
    Settings settings;
    File_Vocabulary file_voc;
    ;



    #endif


    Menu_settings.cpp



    #include "Menu_settings.h"

    namespace voc

    Menu_settings::Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
    :filename_settingsfile_settings,settingsset, file_vocfv

    _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
    _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

    enum class Menu_settings_choice
    change_color = 1,
    set_learned = 2,
    reset_learned = 3,
    set_languges = 4,
    exit = 5
    ;

    while (true)
    Menu_settings_choice choice = static_cast<Menu_settings_choice>(get_int(1, 5,
    bissmann_print +
    L"n Enter a number to choose an option:"
    "n [1] Change color of the background or the letters"
    "n [2] Define how often to repeat a word until it counts as learned"
    "n [3] Set all vocabulary to not learned"
    "n [4] Define target and source language"
    "n [5] Return to main menue"
    "n"));

    switch (choice)
    case Menu_settings_choice::change_color: menu_change_color(); break;
    case Menu_settings_choice::set_learned: menu_set_learned(); break;
    case Menu_settings_choice::reset_learned: menu_reset_learned(); break;
    case Menu_settings_choice::set_languges: menu_set_languages(); break;
    case Menu_settings_choice::exit: return;




    void Menu_settings::menu_change_color()


    void Menu_settings::menu_set_learned()

    std::wostringstream ost;
    ost << "n Currently each vocabulary has to be translated right"
    "n " << settings.threshold_learned() << " times in a row to make it count as learned."
    "n Enter a new value for practiced right. Range: 1 - 99n";

    settings.set_threshold_learned(get_int(1, 99, bissmann_print + ost.str()));
    write_settings_to_file(filename_settings, settings);


    void Menu_settings::menu_reset_learned()

    int choice = get_int(1, 2,
    bissmann_print +
    L"n Choose an option:"
    "n [1] Reset all words to not learned. Practice right = 0"
    "n [2] Return to settingsn");

    if (choice == 2) return;

    choice = get_int(1, 2,
    bissmann_print +
    L"n Are you SUPER SUPER SURE??"
    "n [1] Reset all words to not learned. Practice right = 0"
    "n [2] Return to settingsn");

    if (choice == 2) return;

    file_voc.reset_learned();
    std::wcout<<"n All words changed to not learnedn";
    utility::keep_window_open(L"q");


    void Menu_settings::menu_set_languages()

    enum class Menu_change_language_choice
    change_source_language = 1,
    change_target_language = 2,
    return_to_settings = 3
    ;

    std::wostringstream ost;
    ost<< "n Currently all words need to be translated from "<<settings.name_of_source_language()<<" to "<< settings.name_of_target_language()<<
    "n Choose an option to change the language"
    "n [1] Change the source language ("<< settings.name_of_source_language() <<")"
    "n [2] Change the target language ("<< settings.name_of_target_language()<<")"
    "n [3] Return to settingsn";

    Menu_change_language_choice choice = static_cast<Menu_change_language_choice>(get_int(1, 3,bissmann_print + ost.str()));

    if (choice == Menu_change_language_choice::return_to_settings) return;
    std::cin.ignore();

    switch (choice)
    case Menu_change_language_choice::change_source_language:
    std::wcout << "Enter the new source language:n";
    std::wstring in;
    getline(std::wcin,in);

    if (in.empty())
    std::wcerr << "Invalid empty string enteredn";
    utility::keep_window_open(L"q");
    return;


    settings.set_name_of_source_language(in);
    write_settings_to_file(filename_settings, settings);
    std::wcout<<"n New source language saved to settings";
    utility::keep_window_open(L"q");
    break;

    case Menu_change_language_choice::change_target_language:
    std::wcout << "Enter the new target language:n";
    std::wstring in;
    getline(std::wcin, in);

    if (in.empty())
    std::wcerr << "Invalid empty string enteredn";
    utility::keep_window_open(L"q");
    return;


    settings.set_name_of_target_language(in);
    write_settings_to_file(filename_settings, settings);
    std::cout<<"n New target language saved to settings";
    utility::keep_window_open(L"q");
    break;

    case Menu_change_language_choice::return_to_settings:
    return;





    Menu_show.h



    #ifndef MENU_SHOW_GUARD170420182020
    #define MENU_SHOW_GUARD170420182020

    #pragma once

    #include <string>
    #include <iostream>
    #include <sstream>
    #include <fcntl.h>
    #include <io.h>
    #include <iomanip>

    #include "Settings.h"
    #include "misc.h"
    #include "File_Vocabulary.h"

    namespace voc
    class Menu_show
    public:
    Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
    private:
    enum class Menu_show_choice
    start,
    search,
    erase,
    exit
    ;

    void menu_search();
    void menu_erase();

    std::string filename_settings;
    Settings settings;
    File_Vocabulary file_voc;
    Menu_show_choice step;
    int delete_row;

    static constexpr int rows_per_page = 15;
    static constexpr char left_arrow = 75;
    static constexpr char right_arrow = 77;

    static constexpr int max_len_of_display = 35; // complete window = 80
    ;



    #endif


    Menu_show.cpp



    #include "Menu_show.h"

    namespace voc

    Menu_show::Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
    :filename_settings file_settings , settings set , file_voc fv ,stepMenu_show_choice::start,delete_row0

    _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
    _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

    int page = 1;
    while (true)
    if (file_voc.size() < 1)
    clear_screen();
    std::wcout << "n Database is emptyn";
    utility::keep_window_open(L"q");
    return;



    int count_of_pages = file_voc.size() / rows_per_page;
    if ((file_voc.size() % rows_per_page) != 0) //if fracture there must be one more page
    ++count_of_pages;

    clear_screen();
    std::wcout << "row ";
    std::wcout << settings.name_of_source_language() << ':';

    // check what is possible max len and then break to the next line
    for (size_t i = 0; i < max_len_of_display - settings.name_of_source_language().size(); ++i) //max len = longest word in database !!!
    std::wcout << ' ';

    std::wcout << settings.name_of_target_language() << ':';

    for (size_t i = 0; i < max_len_of_display - settings.name_of_target_language().size(); ++i) //max len = longest word in database !!!
    std::wcout << ' ';

    std::wcout << "OK ";

    for (int i = 0; i < 80; ++i)
    std::wcout << '-';

    int start_row = 0 + (page - 1) * rows_per_page;
    int end_row = 0;
    if (page == count_of_pages)
    end_row = file_voc.size()-1;
    else
    end_row = start_row + rows_per_page - 1;

    //read vocabulary from file in chunks
    std::vector<Vocabulary> print_voc file_voc.get_part(start_row, end_row) ;

    for (size_t i=0; i<print_voc.size(); ++i)
    std::wcout << std::setw(4) << std::left << start_row+1 + i
    << "
    for (int i = 0; i < 80; ++i)
    std::wcout << '-';

    if (page != 1) std::wcout << "<-";
    else std::wcout << " ";
    if (page != count_of_pages) std::wcout << " ->";
    else std::wcout << " ";
    std::wcout << " page " << page << " of " << count_of_pages;

    switch (step)
    case Menu_show_choice::exit:
    return;
    case Menu_show_choice::start:
    std::wcout <<
    "n Choose an option:"
    "n [1] Search for a word to delete it"
    "n [2] Return to main menue"
    "n";
    int choice = read_single_char();
    if (choice == '1')
    step = Menu_show_choice::search;
    else if (choice == '2') // Back to main Menue
    return;
    else
    choice = read_single_char(); //Call twice because first time return always 0xE0 second returns code
    step = Menu_show_choice::start;

    if (choice == left_arrow && page != 1)
    --page;
    if (choice == right_arrow && page != count_of_pages)
    ++page;
    break;

    case Menu_show_choice::search: menu_search();
    break;
    case Menu_show_choice::erase: menu_erase();
    break;




    void Menu_show::menu_search() //ugly better with a class

    std::wcout << "n Enter a word or the specific row to searchn a entry in the vocabulary database:n";
    std::wstring input;
    std::wcin.ignore();
    std::getline(std::wcin, input);

    bool is_number = true;

    try
    delete_row = std::stoi(input);
    --delete_row;//because humans start on 1 not on 0

    catch (...) // catch stoi std::invalid_argument std::out_of_range
    is_number = false;


    if (!is_number) //search by name
    delete_row = file_voc.find(input); // ??? delete row == 0

    if (delete_row == 0) //No match
    std::wcout << "n No match found in filen";
    utility::keep_window_open(L"q");
    step = Menu_show_choice::start; // break and set global menu
    return;


    else // search by row number
    if (delete_row < 0
    step = Menu_show_choice::erase; // go to next step erase


    void Menu_show::menu_erase()

    Vocabulary delete_voc;
    delete_voc = file_voc.get_row(delete_row); // get word to delte for display

    int choice = 0;
    bool repeat = false;

    for (int i = 0; i <2; ++i) "
    << std::setw(max_len_of_display) << std::left << delete_voc.target().substr(0, max_len_of_display)
    << "
    file_voc.erase(delete_row);
    std::wcout << "n Hasta la vista baby!!!!!"
    << "n Entry has been terminated.n";
    utility::keep_window_open(L"q");

    step = Menu_show_choice::start;




    misc.h



    #ifndef MISC_GUARD120420181946
    #define MISC_GUARD120420181946

    #pragma once

    #include <string>
    #include <sstream>
    #include <map>
    #include <cctype>
    #include <cwctype>
    #include <iostream>
    #include <conio.h>
    #include <Windows.h>

    #include "Utility.h"

    namespace voc

    const std::wstring bissmann_print =

    L"n--------Bißmann's-------"
    "n---Vocabulary Trainer---"
    "n---------V3.0-----------"
    "n"
    ;

    void clear_screen(); //Windows only not portable
    int read_single_char(void);
    void change_color(char background, char font); //Windows only not portable
    std::wstring hex_to_color(int hex);
    char hex_to_ascii(char c);

    // functions to safely read in an integer
    void skip_to_int();
    int get_int();
    int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry = L"");

    std::vector<std::wstring> get_words();


    #endif


    misc.cpp



    #include "misc.h"

    namespace voc

    void clear_screen()

    system("cls"); //WINDOWS ONLY


    int read_single_char(void)

    return _getch(); //WINDOWS ONLY


    void change_color(char background, char font)

    std::string color = "COLOR ";
    color.push_back(hex_to_ascii(background));
    color.push_back(hex_to_ascii(font));
    system(color.c_str()); //WINDOWS ONLY


    std::wstring hex_to_color(int hex)

    const std::vector<std::wstring> color_hex =

    L"Black",
    L"Blue",
    L"Green",
    L"Aqua",
    L"Red",
    L"Purple",
    L"Yellow",
    L"White",
    L"Gray",
    L"Light Blue",
    L"Light Green",
    L"Light Aqua",
    L"Light Red",
    L"Light Purple",
    L"Light Yellow",
    L"Bright White"
    ;

    return color_hex[hex];


    char hex_to_ascii(char c)

    if (c < 10) // convert number from hex to ascii. different offsets for 1-9 and a-z in ascii file
    c += 48;
    else
    c += 55;
    return c;


    void skip_to_int()

    if (std::wcin.fail()) // we found sth that wasnt an integer
    std::wcin.clear(); // wed like to look at the characters
    for (wchar_t ch; std::wcin >> ch;) ch == '-')
    std::wcin.unget(); //put the digit back so that we can read the number
    return;






    int get_int()

    int n = 0;
    while (true)
    if (std::wcin >> n) return n;
    skip_to_int();



    int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry)

    while (true)
    clear_screen();
    std::wcout << greeting;

    int n = get_int();
    if (low <= n && n <= high) return n;
    if (!sorry.empty())
    std::wcout << sorry;
    utility::keep_window_open();




    std::vector<std::wstring> get_words()
    //reads in line and returns the individual words of it

    std::wstring line;
    std::getline(std::wcin, line);

    std::wistringstream iss line ;
    std::vector<std::wstring> ret;

    for (std::wstring in; iss >> in;)
    ret.push_back(in);
    return ret;




    settings.h



    #ifndef SETTINGS_GUARD110420181833
    #define SETTINGS_GUARD110420181833

    #pragma once

    #include <string>
    #include <fstream>
    #include <fcntl.h>
    #include <io.h>

    namespace voc

    class Settings
    public:
    Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font);

    void set_threshold_learned(int threshold) thr_learnd = threshold;
    void set_name_of_source_language(const std::wstring& source_name) s_language = source_name;
    void set_name_of_target_language(const std::wstring& target_name) t_language = target_name;
    void set_color_background(int back) clr_back = back;
    void set_color_font(int font) clr_font = font;

    int threshold_learned() const return thr_learnd; ;
    std::wstring name_of_source_language() const return s_language;
    std::wstring name_of_target_language() const return t_language;
    char color_background() const return clr_back;
    char color_font() const return clr_font;
    private:
    int thr_learnd; //learned
    std::wstring s_language;
    std::wstring t_language;
    char clr_back;
    char clr_font;
    ;

    std::wostream& operator<<(std::wostream& os, const Settings& obj);
    std::wistream& operator>>(std::wistream& is, Settings& obj);

    void write_settings_to_file(const std::string& filename, Settings& data);
    bool read_settings_from_file(const std::string& filename, Settings& data);


    #endif


    Settings.cpp



    #include "Settings.h"

    namespace voc
    Settings::Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font)
    :thr_learnd threshold ,
    s_language source_name ,
    t_language target_name ,
    clr_back color_back ,
    clr_font color_font
    color_back > 15

    //____________________________Helper functions
    std::wostream& operator<<(std::wostream& os, const Settings& obj)


    os << obj.threshold_learned() << 't'
    << obj.name_of_source_language() << 't'
    << obj.name_of_target_language() << 't'
    << static_cast<int>(obj.color_background()) << 't'
    << static_cast<int>(obj.color_font());

    return os;

    std::wistream& operator>>(std::wistream& is, Settings& obj)

    int learned;
    std::wstring source;
    std::wstring target;
    int back;
    int font;

    is >> learned >> source >> target >> back >> font;

    Settings ret(learned, source, target, static_cast<char>(back), static_cast<char>(font));
    obj = ret;

    return is;


    void write_settings_to_file(const std::string& filename, Settings& data)

    std::wofstream ofs filename ;
    ofs << data;



    bool read_settings_from_file(const std::string& filename, Settings& data)

    std::wifstream ifs filename ;
    if (!ifs) return false;

    ifs >> data;
    return true;




    Utility.h



    #ifndef UTILITY_GUARD11042018
    #define UTILITY_GUARD11042018

    #pragma once

    #include <iostream>
    #include <string>
    #include <random>
    #include <chrono>

    namespace utility

    inline void keep_window_open(std::wstring s)

    if (s == L"") return;
    std::wcin.clear();
    std::wcin.ignore(120, 'n');
    for (;;)
    std::wcout << "Please enter " << s << " to exitn";
    std::wstring ss;
    while (std::wcin >> ss && ss != s)
    std::wcout << "Please enter " << s << " to exitn";
    return;



    inline void keep_window_open()

    std::wcin.clear();
    std::wcout << "Please enter a character to exitn";
    wchar_t ch;
    std::wcin >> ch;
    return;


    int get_random(int min, int max);


    #endif


    Utility.cpp



    #include "Utility.h"


    namespace utility

    int get_random(int min, int max)

    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_int_distribution<int> distribution(min, max);
    return distribution(mt);




    Vocabulary.h



    #ifndef VOCABULARY_GUARD100420181927
    #define VOCABULARY_GUARD100420181927

    #pragma once

    #include <string>
    #include <vector>
    #include <sstream>

    namespace voc

    class Vocabulary
    public:
    Vocabulary();
    Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar);
    Vocabulary(const std::wstring& org, const std::wstring& tar);

    std::vector<std::wstring> source_words()const return src_words;
    std::vector<std::wstring> target_words()const return tar_words;

    std::wstring source()const;
    std::wstring target()const;

    int practiced_right() const return pr;

    void set_source_words(const std::vector<std::wstring>& org) src_words = org;
    void set_target_words(const std::vector<std::wstring>& tar) tar_words = tar;
    void set_practiced_right(int p) pr = p;
    private:
    std::vector<std::wstring> src_words;
    std::vector<std::wstring> tar_words;
    int pr; //practiced_right
    ;

    std::wostream& operator<<(std::wostream& os, const Vocabulary& obj);
    std::wistream& operator>>(std::wistream& is, Vocabulary& obj);
    inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs);

    std::vector<std::wstring> extract_words(const std::wstring& line);
    std::wstring words_to_line(const std::vector<std::wstring>& sv);


    #endif


    Vocabulary.cpp



    #include "Vocabulary.h"

    namespace voc
    //____________________________Member functions
    Vocabulary::Vocabulary()
    :pr0



    Vocabulary::Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar)
    :src_wordsorg,tar_wordstar,pr0



    Vocabulary::Vocabulary(const std::wstring& org, const std::wstring& tar)
    : src_words extract_words(org) ,tar_words extract_words(tar) ,pr0



    std::wstring Vocabulary::source()const return words_to_line(src_words);
    std::wstring Vocabulary::target()const return words_to_line(tar_words);

    //____________________________Helper functions
    std::wostream& operator<<(std::wostream& os, const Vocabulary& obj)

    for (const auto& x : obj.source_words())
    os << x + L" ";
    os << "# ";
    for (const auto& x : obj.target_words())
    os << x + L" ";
    os << "# ";
    os << obj.practiced_right();

    return os;

    std::wistream& operator>>(std::wistream& is, Vocabulary& obj)

    std::wstring line;
    std::getline(is,line);
    std::wistringstream ifs line ;

    std::vector<std::wstring> org;
    bool first = false;
    for (std::wstring s; ifs >> s;)
    if (s == L"#")
    first = true;
    break;

    org.push_back(s);


    if (!first) // end of stream but first # not found
    is.setstate(std::ios::failbit);
    return is;


    std::vector<std::wstring> tar;
    bool second = false;
    for (std::wstring s; ifs >> s;)
    if (s == L"#")
    second = true;
    break;

    tar.push_back(s);


    if (!second) // same for second
    is.setstate(std::ios::failbit);
    return is;


    std::wstring s;
    ifs >> s;
    obj.set_source_words(org);
    obj.set_target_words(tar);
    obj.set_practiced_right(std::stoi(s));

    return is;


    inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs)
    // no check of valid member

    return (lhs.source_words() == rhs.target_words()) && (lhs.target_words() == rhs.target_words());


    std::vector<std::wstring> extract_words(const std::wstring& line)

    std::wistringstream ifs line ;
    std::vector<std::wstring> ret;
    for (std::wstring s; ifs >> s;)
    ret.push_back(s);
    return ret;;


    std::wstring words_to_line(const std::vector<std::wstring>& sv)
    //makes a vector of strings to a line seperated by whitespace

    std::wstring ret;

    for (const auto&x : sv)
    ret = ret + x + L' ';
    if (!ret.empty()) // remove last ' '
    ret.pop_back();
    return ret;




    main.cpp



    #include <iostream>
    #include <string>

    #include "Menu.h"


    const std::string filename_settings = "settings.txt";
    const std::string filename_vocabulary = "vocsafe.txt";

    int main()
    try
    voc::Menu menu(filename_settings, filename_vocabulary);

    catch (std::runtime_error& e)
    std::wcerr << e.what() << "n";
    utility::keep_window_open(L"~");

    catch (...)
    std::wcerr << "unknown error " << "n";
    utility::keep_window_open(L"~");







    share|improve this question























      up vote
      7
      down vote

      favorite









      up vote
      7
      down vote

      favorite











      I wrote a console based Vocabulary Trainer to practice C++.



      Its possible to add and remove vocabulary and practice it with it.



      I save and read the vocabulary from a file to be able to read it.



      Also certain settings like target and source language color of the console get saved in a settings file.



      I know currently it is not portable because I use Windows functions.



      I tested it on a Mexican Windows 10 system and a German Windows 7 system.



      I wonder if I'm handling the widestrings correctly. During the development I got into the problem that special signs like öäüß don't get displayed properly, so I switched to widestrings in the program. I wonder if my approach is right for this.



      Also, I wonder if it's a good idea to make classes for the Menus.



      In the next step I want to make this a GUI application to practice making GUIs, so I think it's a good time to review the code. Feel free to comment on any part you like.



      File_Vocabulary.h



      #ifndef FILE_VOCABULARY_GUARD100420181849
      #define FILE_VOCABULARY_GUARD100420181849

      #pragma once

      #include <fstream>
      #include <sstream>
      #include <cstdio>
      #include <fcntl.h>
      #include <io.h>

      #include "Vocabulary.h"

      namespace voc

      class File_Vocabulary
      public:
      File_Vocabulary(const std::string& in_filename);

      void add(const Vocabulary& v);
      bool erase(const std::wstring& s); // erase by word
      bool erase(const int row); // erasse by row
      bool is_match(const Vocabulary& v); // checks if vocabulary is exactly like this in file

      int find(const std::vector<std::wstring> s); // checks if words are in file and returns row of it. 0 if no row
      int find(std::wstring s); // checks if words are in file and returns row of it. 0 if no row

      std::vector<Vocabulary> get_part(const int begin, const int end); // return in chuncks specify how many rows of Vocabulary you want
      Vocabulary get_row(const int row); // return row
      int size()const return count;

      size_t max_size_source()const;
      size_t max_size_target()const;

      void reset_learned(); //resets learned to 0 in all vocabulary
      private:
      std::string filename;
      int count; //count of vocabulary;
      ;


      #endif


      File_Vocabulary.cpp



      #include "File_Vocabulary.h"

      namespace voc

      //____________________________Member functions

      File_Vocabulary::File_Vocabulary(const std::string& in_filename)
      :filenamein_filename,count0

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      std::wifstream ifs filename ;
      if (!ifs) return; // file doesnt exist yet

      for (Vocabulary curr; ifs >> curr;)
      ++count;



      void File_Vocabulary::add(const Vocabulary& v)

      const std::string out_filename = "tmp_" + filename;
      //to close the files
      std::wifstream ifs filename ;
      if (ifs) // dont check if file doesnst exist yet

      std::wofstream ofs out_filename ;
      bool added = false;

      for (Vocabulary curr; ifs >> curr;)
      if (curr.source_words() == v.source_words() && !added) //replace definition of vocabulary
      curr.set_target_words(v.target_words());
      curr.set_practiced_right(v.practiced_right());
      added = true;

      else if (curr.source_words() > v.source_words() && !added)
      ofs << v << 'n';
      ++count;
      added = true;

      ofs << curr << 'n';


      if (!added) ofs << v << 'n'; // case file was empty


      else //special case first word to add
      std::wofstream ofs filename ;
      ofs << v << 'n';
      return;


      std::remove(filename.c_str());
      std::rename(out_filename.c_str(), filename.c_str());


      bool File_Vocabulary::erase(const std::wstring& s) //search by word to erase

      const std::string out_filename = "tmp_" + filename;
      bool erased = false;

      std::wifstream ifs filename ;
      if (!ifs) return false;

      std::wofstream ofs out_filename ;

      for (Vocabulary curr; ifs >> curr;)
      if ((curr.source_words() == extract_words(s)


      if (std::remove(filename.c_str()) != 0)
      throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
      "file operation delete old vocabulary failedn");
      if (std::rename(out_filename.c_str(), filename.c_str()) != 0)
      throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
      "file operation rename new to old failedn");

      return erased;


      bool File_Vocabulary::erase(const int row)


      bool File_Vocabulary::is_match(const Vocabulary& v) // checks if vocabulary is exactly like this in file

      std::wifstream ifs filename ;
      if (!ifs) return false;

      for (Vocabulary curr; ifs >> curr;)
      if (v.source_words() == curr.source_words())
      if (v.target_words() == curr.target_words())
      return true;
      else
      return false;


      throw std::runtime_error("bool File_Vocabulary::erase(const int row)nDatabase corrupt. Original word is not in database.n");
      return false;


      int File_Vocabulary::find(const std::vector<std::wstring> s) //checks if word is in file and returns row of it

      std::wifstream ifs filename ;
      if (!ifs) return 0;

      int row = 0;
      for (Vocabulary curr; ifs >> curr;)
      if (curr.source_words() == s
      return -1;


      int File_Vocabulary::find(std::wstring s) //checks if word is in file and returns row of it

      std::wistringstream ist s ;

      std::vector<std::wstring> vs;
      for (std::wstring in; ist >> in;)
      vs.push_back(in);

      return find(vs); // call the other find routine


      std::vector<Vocabulary> File_Vocabulary::get_part(const int begin, const int end) //return in chuncks specify how many rows of Vocabulary you want


      Vocabulary File_Vocabulary::get_row(const int row) // return row

      std::vector<Vocabulary> ret = get_part(row, row);
      return *ret.begin();


      void File_Vocabulary::reset_learned()

      std::wifstream ifs filename ;
      if (!ifs) return;

      const std::string out_filename = "tmp_" + filename;
      std::wofstream ofs out_filename ;

      for (Vocabulary curr; ifs >> curr;)
      curr.set_practiced_right(0);
      ofs << curr << 'n';


      std::remove(filename.c_str());
      std::rename(out_filename.c_str(), filename.c_str());


      size_t File_Vocabulary::max_size_source() const

      std::wifstream ifs filename ;
      if (!ifs) return 0;

      size_t ret = 0;
      for (Vocabulary curr; ifs >> curr;)
      if (curr.source_words().size() > ret)
      ret = curr.source_words().size();

      return ret;


      size_t File_Vocabulary::max_size_target() const

      std::wifstream ifs filename ;
      if (!ifs) return 0;

      size_t ret = 0;
      for (Vocabulary curr; ifs >> curr;)
      if (curr.target_words().size() > ret)
      ret = curr.target_words().size();

      return ret;




      Menu.h



      #ifndef MENU_GUARD110420181806
      #define MENU_GUARD110420181806

      #pragma once

      #include <iostream>
      #include <sstream>
      #include <fcntl.h>
      #include <io.h>

      #include "Menu_settings.h"
      #include "Menu_show.h"

      namespace voc
      class Menu
      public:
      Menu(const std::string& file_settings, const std::string& file_vocabulary);
      private:
      void init_settings();
      void menu_main();
      void menu_practice();
      void menu_add();

      File_Vocabulary file_voc;
      Settings settings;
      std::string fname_settings;
      std::string fname_vocabulary;
      ;


      #endif


      Menu.cpp



      #include"Menu.h"

      namespace voc
      //____________________________Member functions
      Menu::Menu(const std::string& file_settings, const std::string& file_vocabulary)
      :settings 10, L"spanish", L"german", 0, 15 ,
      file_voc file_vocabulary ,
      fname_settingsfile_settings,
      fname_vocabularyfile_vocabulary

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      init_settings();
      menu_main();


      void Menu::init_settings()
      // try to read settings from file
      // if not take default values and write to new file
      // change color from settings

      if (!read_settings_from_file(fname_settings, settings))
      write_settings_to_file(fname_settings, settings);

      change_color(settings.color_background(), settings.color_font());


      void Menu::menu_main()

      enum class Menu_main_choice
      practice = 1,
      add = 2,
      show = 3,
      settings = 4,
      exit = 5
      ;

      while (true)
      Menu_main_choice choice = static_cast<Menu_main_choice>(get_int(1, 5,
      bissmann_print +
      L"n Enter a number to choose an option:"
      "n [1] Practice vocabulary"
      "n [2] Add new vocabulary to database"
      "n [3] Show all words currently in the database. Delete words in database"
      "n [4] Settings"
      "n [5] End program"
      "n"));

      switch (choice)
      case Menu_main_choice::practice: menu_practice();break;
      case Menu_main_choice::add: menu_add(); break;
      case Menu_main_choice::show:
      Menu_show ms fname_settings,settings ,file_voc ;
      break;

      case Menu_main_choice::settings:
      Menu_settings ms fname_settings,settings ,file_voc ;
      settings = ms.get_settings();
      break;

      case Menu_main_choice::exit: return;




      void Menu::menu_practice()

      clear_screen();
      int count_of_rows = file_voc.size()-1;
      if (count_of_rows == 0) // file empty fill first with vocabulary
      std::wcout << "n Error: Database is empty. Fill database first with vocabularyn";
      utility::keep_window_open(L"q");
      return;


      int amount = get_int(1, 1000,bissmann_print +L"n Enter how many words you want to practice.Range 1 - 1000 n");

      Vocabulary rnd_voc; // vocabulary
      int correct_words = 0;
      for (int i = 1; i <= amount; ++i)
      clear_screen();
      std::wcout <<bissmann_print;

      int repeat_rnd = 0;

      do
      rnd_voc = file_voc.get_row(utility::get_random(1, count_of_rows));
      ++repeat_rnd;
      if (repeat_rnd == 1000) //assuming after 1000 rolls no valid word could be found so all are "learned"
      std::wcout << "n You learned all vocabulary by repeating each word " << settings.threshold_learned() << " times correct n";
      utility::keep_window_open(L"q");
      return;

      while (rnd_voc.practiced_right() >= settings.threshold_learned());

      clear_screen();
      std::wcout << bissmann_print
      << "n Word nummber " << i << " out of " << amount << ":n"
      << "n The " << settings.name_of_source_language() << " word is:nn " << rnd_voc.source() << "n"
      << "n Enter the translation in " << settings.name_of_target_language() << ":nn";

      std::wcin.ignore();
      std::vector<std::wstring> input_words = get_words();

      if (input_words == rnd_voc.target_words()) //word translated right
      ++correct_words;

      int practiced_right = rnd_voc.practiced_right();
      ++practiced_right;
      rnd_voc.set_practiced_right(practiced_right);

      std::wcout << "n That is correct!!!n";

      if (rnd_voc.practiced_right() >= settings.threshold_learned())
      std::wcout << "n You learned the word. You answered it correct " << rnd_voc.practiced_right() << " timesn";

      else //translated wrong
      rnd_voc.set_practiced_right(0);
      std::wcout << "n That is wrong !!!"
      << "n The correct translation is: " << rnd_voc.target()<<'n';

      utility::keep_window_open(L"q");
      file_voc.add(rnd_voc);

      clear_screen();
      std::wcout << bissmann_print
      << "n You translatet " << correct_words << " out of " << amount << " words correctly to " << settings.name_of_target_language()<<'n';
      utility::keep_window_open(L"q");
      return;


      void Menu::menu_add()

      while (true)
      clear_screen();
      int choice = get_int(1, 2,
      bissmann_print +
      L"n [1] Add a new word to the database"
      "n [2] Return to main menue"
      "n");
      if (choice == 2) return;

      clear_screen();
      std::wcout << bissmann_print << "n Enter the " << settings.name_of_source_language() << " wordn";

      Vocabulary add_voc;
      std::wcin.ignore();
      std::vector<std::wstring> org_words = get_words();
      add_voc.set_source_words(org_words);

      clear_screen();
      std::wcout << bissmann_print << "n Enter the translation for '" << add_voc.source() << "' in " << settings.name_of_target_language() << 'n';

      std::vector<std::wstring> tar_words = get_words();
      add_voc.set_target_words(tar_words);

      file_voc.add(add_voc);






      Menu_settings.h



      #ifndef MENU_SETTINGS_GUARD160420182054
      #define MENU_SETTINGS_GUARD160420182054

      #pragma once

      #include <string>
      #include <iostream>
      #include <sstream>
      #include <fcntl.h>
      #include <io.h>

      #include "Settings.h"
      #include "misc.h"
      #include "File_Vocabulary.h"

      namespace voc
      class Menu_settings
      public:
      Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
      Settings get_settings() const return settings;
      private:
      void menu_change_color();
      void menu_set_learned();
      void menu_reset_learned();
      void menu_set_languages();

      std::string filename_settings;
      Settings settings;
      File_Vocabulary file_voc;
      ;



      #endif


      Menu_settings.cpp



      #include "Menu_settings.h"

      namespace voc

      Menu_settings::Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
      :filename_settingsfile_settings,settingsset, file_vocfv

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      enum class Menu_settings_choice
      change_color = 1,
      set_learned = 2,
      reset_learned = 3,
      set_languges = 4,
      exit = 5
      ;

      while (true)
      Menu_settings_choice choice = static_cast<Menu_settings_choice>(get_int(1, 5,
      bissmann_print +
      L"n Enter a number to choose an option:"
      "n [1] Change color of the background or the letters"
      "n [2] Define how often to repeat a word until it counts as learned"
      "n [3] Set all vocabulary to not learned"
      "n [4] Define target and source language"
      "n [5] Return to main menue"
      "n"));

      switch (choice)
      case Menu_settings_choice::change_color: menu_change_color(); break;
      case Menu_settings_choice::set_learned: menu_set_learned(); break;
      case Menu_settings_choice::reset_learned: menu_reset_learned(); break;
      case Menu_settings_choice::set_languges: menu_set_languages(); break;
      case Menu_settings_choice::exit: return;




      void Menu_settings::menu_change_color()


      void Menu_settings::menu_set_learned()

      std::wostringstream ost;
      ost << "n Currently each vocabulary has to be translated right"
      "n " << settings.threshold_learned() << " times in a row to make it count as learned."
      "n Enter a new value for practiced right. Range: 1 - 99n";

      settings.set_threshold_learned(get_int(1, 99, bissmann_print + ost.str()));
      write_settings_to_file(filename_settings, settings);


      void Menu_settings::menu_reset_learned()

      int choice = get_int(1, 2,
      bissmann_print +
      L"n Choose an option:"
      "n [1] Reset all words to not learned. Practice right = 0"
      "n [2] Return to settingsn");

      if (choice == 2) return;

      choice = get_int(1, 2,
      bissmann_print +
      L"n Are you SUPER SUPER SURE??"
      "n [1] Reset all words to not learned. Practice right = 0"
      "n [2] Return to settingsn");

      if (choice == 2) return;

      file_voc.reset_learned();
      std::wcout<<"n All words changed to not learnedn";
      utility::keep_window_open(L"q");


      void Menu_settings::menu_set_languages()

      enum class Menu_change_language_choice
      change_source_language = 1,
      change_target_language = 2,
      return_to_settings = 3
      ;

      std::wostringstream ost;
      ost<< "n Currently all words need to be translated from "<<settings.name_of_source_language()<<" to "<< settings.name_of_target_language()<<
      "n Choose an option to change the language"
      "n [1] Change the source language ("<< settings.name_of_source_language() <<")"
      "n [2] Change the target language ("<< settings.name_of_target_language()<<")"
      "n [3] Return to settingsn";

      Menu_change_language_choice choice = static_cast<Menu_change_language_choice>(get_int(1, 3,bissmann_print + ost.str()));

      if (choice == Menu_change_language_choice::return_to_settings) return;
      std::cin.ignore();

      switch (choice)
      case Menu_change_language_choice::change_source_language:
      std::wcout << "Enter the new source language:n";
      std::wstring in;
      getline(std::wcin,in);

      if (in.empty())
      std::wcerr << "Invalid empty string enteredn";
      utility::keep_window_open(L"q");
      return;


      settings.set_name_of_source_language(in);
      write_settings_to_file(filename_settings, settings);
      std::wcout<<"n New source language saved to settings";
      utility::keep_window_open(L"q");
      break;

      case Menu_change_language_choice::change_target_language:
      std::wcout << "Enter the new target language:n";
      std::wstring in;
      getline(std::wcin, in);

      if (in.empty())
      std::wcerr << "Invalid empty string enteredn";
      utility::keep_window_open(L"q");
      return;


      settings.set_name_of_target_language(in);
      write_settings_to_file(filename_settings, settings);
      std::cout<<"n New target language saved to settings";
      utility::keep_window_open(L"q");
      break;

      case Menu_change_language_choice::return_to_settings:
      return;





      Menu_show.h



      #ifndef MENU_SHOW_GUARD170420182020
      #define MENU_SHOW_GUARD170420182020

      #pragma once

      #include <string>
      #include <iostream>
      #include <sstream>
      #include <fcntl.h>
      #include <io.h>
      #include <iomanip>

      #include "Settings.h"
      #include "misc.h"
      #include "File_Vocabulary.h"

      namespace voc
      class Menu_show
      public:
      Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
      private:
      enum class Menu_show_choice
      start,
      search,
      erase,
      exit
      ;

      void menu_search();
      void menu_erase();

      std::string filename_settings;
      Settings settings;
      File_Vocabulary file_voc;
      Menu_show_choice step;
      int delete_row;

      static constexpr int rows_per_page = 15;
      static constexpr char left_arrow = 75;
      static constexpr char right_arrow = 77;

      static constexpr int max_len_of_display = 35; // complete window = 80
      ;



      #endif


      Menu_show.cpp



      #include "Menu_show.h"

      namespace voc

      Menu_show::Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
      :filename_settings file_settings , settings set , file_voc fv ,stepMenu_show_choice::start,delete_row0

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      int page = 1;
      while (true)
      if (file_voc.size() < 1)
      clear_screen();
      std::wcout << "n Database is emptyn";
      utility::keep_window_open(L"q");
      return;



      int count_of_pages = file_voc.size() / rows_per_page;
      if ((file_voc.size() % rows_per_page) != 0) //if fracture there must be one more page
      ++count_of_pages;

      clear_screen();
      std::wcout << "row ";
      std::wcout << settings.name_of_source_language() << ':';

      // check what is possible max len and then break to the next line
      for (size_t i = 0; i < max_len_of_display - settings.name_of_source_language().size(); ++i) //max len = longest word in database !!!
      std::wcout << ' ';

      std::wcout << settings.name_of_target_language() << ':';

      for (size_t i = 0; i < max_len_of_display - settings.name_of_target_language().size(); ++i) //max len = longest word in database !!!
      std::wcout << ' ';

      std::wcout << "OK ";

      for (int i = 0; i < 80; ++i)
      std::wcout << '-';

      int start_row = 0 + (page - 1) * rows_per_page;
      int end_row = 0;
      if (page == count_of_pages)
      end_row = file_voc.size()-1;
      else
      end_row = start_row + rows_per_page - 1;

      //read vocabulary from file in chunks
      std::vector<Vocabulary> print_voc file_voc.get_part(start_row, end_row) ;

      for (size_t i=0; i<print_voc.size(); ++i)
      std::wcout << std::setw(4) << std::left << start_row+1 + i
      << "
      for (int i = 0; i < 80; ++i)
      std::wcout << '-';

      if (page != 1) std::wcout << "<-";
      else std::wcout << " ";
      if (page != count_of_pages) std::wcout << " ->";
      else std::wcout << " ";
      std::wcout << " page " << page << " of " << count_of_pages;

      switch (step)
      case Menu_show_choice::exit:
      return;
      case Menu_show_choice::start:
      std::wcout <<
      "n Choose an option:"
      "n [1] Search for a word to delete it"
      "n [2] Return to main menue"
      "n";
      int choice = read_single_char();
      if (choice == '1')
      step = Menu_show_choice::search;
      else if (choice == '2') // Back to main Menue
      return;
      else
      choice = read_single_char(); //Call twice because first time return always 0xE0 second returns code
      step = Menu_show_choice::start;

      if (choice == left_arrow && page != 1)
      --page;
      if (choice == right_arrow && page != count_of_pages)
      ++page;
      break;

      case Menu_show_choice::search: menu_search();
      break;
      case Menu_show_choice::erase: menu_erase();
      break;




      void Menu_show::menu_search() //ugly better with a class

      std::wcout << "n Enter a word or the specific row to searchn a entry in the vocabulary database:n";
      std::wstring input;
      std::wcin.ignore();
      std::getline(std::wcin, input);

      bool is_number = true;

      try
      delete_row = std::stoi(input);
      --delete_row;//because humans start on 1 not on 0

      catch (...) // catch stoi std::invalid_argument std::out_of_range
      is_number = false;


      if (!is_number) //search by name
      delete_row = file_voc.find(input); // ??? delete row == 0

      if (delete_row == 0) //No match
      std::wcout << "n No match found in filen";
      utility::keep_window_open(L"q");
      step = Menu_show_choice::start; // break and set global menu
      return;


      else // search by row number
      if (delete_row < 0
      step = Menu_show_choice::erase; // go to next step erase


      void Menu_show::menu_erase()

      Vocabulary delete_voc;
      delete_voc = file_voc.get_row(delete_row); // get word to delte for display

      int choice = 0;
      bool repeat = false;

      for (int i = 0; i <2; ++i) "
      << std::setw(max_len_of_display) << std::left << delete_voc.target().substr(0, max_len_of_display)
      << "
      file_voc.erase(delete_row);
      std::wcout << "n Hasta la vista baby!!!!!"
      << "n Entry has been terminated.n";
      utility::keep_window_open(L"q");

      step = Menu_show_choice::start;




      misc.h



      #ifndef MISC_GUARD120420181946
      #define MISC_GUARD120420181946

      #pragma once

      #include <string>
      #include <sstream>
      #include <map>
      #include <cctype>
      #include <cwctype>
      #include <iostream>
      #include <conio.h>
      #include <Windows.h>

      #include "Utility.h"

      namespace voc

      const std::wstring bissmann_print =

      L"n--------Bißmann's-------"
      "n---Vocabulary Trainer---"
      "n---------V3.0-----------"
      "n"
      ;

      void clear_screen(); //Windows only not portable
      int read_single_char(void);
      void change_color(char background, char font); //Windows only not portable
      std::wstring hex_to_color(int hex);
      char hex_to_ascii(char c);

      // functions to safely read in an integer
      void skip_to_int();
      int get_int();
      int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry = L"");

      std::vector<std::wstring> get_words();


      #endif


      misc.cpp



      #include "misc.h"

      namespace voc

      void clear_screen()

      system("cls"); //WINDOWS ONLY


      int read_single_char(void)

      return _getch(); //WINDOWS ONLY


      void change_color(char background, char font)

      std::string color = "COLOR ";
      color.push_back(hex_to_ascii(background));
      color.push_back(hex_to_ascii(font));
      system(color.c_str()); //WINDOWS ONLY


      std::wstring hex_to_color(int hex)

      const std::vector<std::wstring> color_hex =

      L"Black",
      L"Blue",
      L"Green",
      L"Aqua",
      L"Red",
      L"Purple",
      L"Yellow",
      L"White",
      L"Gray",
      L"Light Blue",
      L"Light Green",
      L"Light Aqua",
      L"Light Red",
      L"Light Purple",
      L"Light Yellow",
      L"Bright White"
      ;

      return color_hex[hex];


      char hex_to_ascii(char c)

      if (c < 10) // convert number from hex to ascii. different offsets for 1-9 and a-z in ascii file
      c += 48;
      else
      c += 55;
      return c;


      void skip_to_int()

      if (std::wcin.fail()) // we found sth that wasnt an integer
      std::wcin.clear(); // wed like to look at the characters
      for (wchar_t ch; std::wcin >> ch;) ch == '-')
      std::wcin.unget(); //put the digit back so that we can read the number
      return;






      int get_int()

      int n = 0;
      while (true)
      if (std::wcin >> n) return n;
      skip_to_int();



      int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry)

      while (true)
      clear_screen();
      std::wcout << greeting;

      int n = get_int();
      if (low <= n && n <= high) return n;
      if (!sorry.empty())
      std::wcout << sorry;
      utility::keep_window_open();




      std::vector<std::wstring> get_words()
      //reads in line and returns the individual words of it

      std::wstring line;
      std::getline(std::wcin, line);

      std::wistringstream iss line ;
      std::vector<std::wstring> ret;

      for (std::wstring in; iss >> in;)
      ret.push_back(in);
      return ret;




      settings.h



      #ifndef SETTINGS_GUARD110420181833
      #define SETTINGS_GUARD110420181833

      #pragma once

      #include <string>
      #include <fstream>
      #include <fcntl.h>
      #include <io.h>

      namespace voc

      class Settings
      public:
      Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font);

      void set_threshold_learned(int threshold) thr_learnd = threshold;
      void set_name_of_source_language(const std::wstring& source_name) s_language = source_name;
      void set_name_of_target_language(const std::wstring& target_name) t_language = target_name;
      void set_color_background(int back) clr_back = back;
      void set_color_font(int font) clr_font = font;

      int threshold_learned() const return thr_learnd; ;
      std::wstring name_of_source_language() const return s_language;
      std::wstring name_of_target_language() const return t_language;
      char color_background() const return clr_back;
      char color_font() const return clr_font;
      private:
      int thr_learnd; //learned
      std::wstring s_language;
      std::wstring t_language;
      char clr_back;
      char clr_font;
      ;

      std::wostream& operator<<(std::wostream& os, const Settings& obj);
      std::wistream& operator>>(std::wistream& is, Settings& obj);

      void write_settings_to_file(const std::string& filename, Settings& data);
      bool read_settings_from_file(const std::string& filename, Settings& data);


      #endif


      Settings.cpp



      #include "Settings.h"

      namespace voc
      Settings::Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font)
      :thr_learnd threshold ,
      s_language source_name ,
      t_language target_name ,
      clr_back color_back ,
      clr_font color_font
      color_back > 15

      //____________________________Helper functions
      std::wostream& operator<<(std::wostream& os, const Settings& obj)


      os << obj.threshold_learned() << 't'
      << obj.name_of_source_language() << 't'
      << obj.name_of_target_language() << 't'
      << static_cast<int>(obj.color_background()) << 't'
      << static_cast<int>(obj.color_font());

      return os;

      std::wistream& operator>>(std::wistream& is, Settings& obj)

      int learned;
      std::wstring source;
      std::wstring target;
      int back;
      int font;

      is >> learned >> source >> target >> back >> font;

      Settings ret(learned, source, target, static_cast<char>(back), static_cast<char>(font));
      obj = ret;

      return is;


      void write_settings_to_file(const std::string& filename, Settings& data)

      std::wofstream ofs filename ;
      ofs << data;



      bool read_settings_from_file(const std::string& filename, Settings& data)

      std::wifstream ifs filename ;
      if (!ifs) return false;

      ifs >> data;
      return true;




      Utility.h



      #ifndef UTILITY_GUARD11042018
      #define UTILITY_GUARD11042018

      #pragma once

      #include <iostream>
      #include <string>
      #include <random>
      #include <chrono>

      namespace utility

      inline void keep_window_open(std::wstring s)

      if (s == L"") return;
      std::wcin.clear();
      std::wcin.ignore(120, 'n');
      for (;;)
      std::wcout << "Please enter " << s << " to exitn";
      std::wstring ss;
      while (std::wcin >> ss && ss != s)
      std::wcout << "Please enter " << s << " to exitn";
      return;



      inline void keep_window_open()

      std::wcin.clear();
      std::wcout << "Please enter a character to exitn";
      wchar_t ch;
      std::wcin >> ch;
      return;


      int get_random(int min, int max);


      #endif


      Utility.cpp



      #include "Utility.h"


      namespace utility

      int get_random(int min, int max)

      std::random_device rd;
      std::mt19937 mt(rd());
      std::uniform_int_distribution<int> distribution(min, max);
      return distribution(mt);




      Vocabulary.h



      #ifndef VOCABULARY_GUARD100420181927
      #define VOCABULARY_GUARD100420181927

      #pragma once

      #include <string>
      #include <vector>
      #include <sstream>

      namespace voc

      class Vocabulary
      public:
      Vocabulary();
      Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar);
      Vocabulary(const std::wstring& org, const std::wstring& tar);

      std::vector<std::wstring> source_words()const return src_words;
      std::vector<std::wstring> target_words()const return tar_words;

      std::wstring source()const;
      std::wstring target()const;

      int practiced_right() const return pr;

      void set_source_words(const std::vector<std::wstring>& org) src_words = org;
      void set_target_words(const std::vector<std::wstring>& tar) tar_words = tar;
      void set_practiced_right(int p) pr = p;
      private:
      std::vector<std::wstring> src_words;
      std::vector<std::wstring> tar_words;
      int pr; //practiced_right
      ;

      std::wostream& operator<<(std::wostream& os, const Vocabulary& obj);
      std::wistream& operator>>(std::wistream& is, Vocabulary& obj);
      inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs);

      std::vector<std::wstring> extract_words(const std::wstring& line);
      std::wstring words_to_line(const std::vector<std::wstring>& sv);


      #endif


      Vocabulary.cpp



      #include "Vocabulary.h"

      namespace voc
      //____________________________Member functions
      Vocabulary::Vocabulary()
      :pr0



      Vocabulary::Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar)
      :src_wordsorg,tar_wordstar,pr0



      Vocabulary::Vocabulary(const std::wstring& org, const std::wstring& tar)
      : src_words extract_words(org) ,tar_words extract_words(tar) ,pr0



      std::wstring Vocabulary::source()const return words_to_line(src_words);
      std::wstring Vocabulary::target()const return words_to_line(tar_words);

      //____________________________Helper functions
      std::wostream& operator<<(std::wostream& os, const Vocabulary& obj)

      for (const auto& x : obj.source_words())
      os << x + L" ";
      os << "# ";
      for (const auto& x : obj.target_words())
      os << x + L" ";
      os << "# ";
      os << obj.practiced_right();

      return os;

      std::wistream& operator>>(std::wistream& is, Vocabulary& obj)

      std::wstring line;
      std::getline(is,line);
      std::wistringstream ifs line ;

      std::vector<std::wstring> org;
      bool first = false;
      for (std::wstring s; ifs >> s;)
      if (s == L"#")
      first = true;
      break;

      org.push_back(s);


      if (!first) // end of stream but first # not found
      is.setstate(std::ios::failbit);
      return is;


      std::vector<std::wstring> tar;
      bool second = false;
      for (std::wstring s; ifs >> s;)
      if (s == L"#")
      second = true;
      break;

      tar.push_back(s);


      if (!second) // same for second
      is.setstate(std::ios::failbit);
      return is;


      std::wstring s;
      ifs >> s;
      obj.set_source_words(org);
      obj.set_target_words(tar);
      obj.set_practiced_right(std::stoi(s));

      return is;


      inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs)
      // no check of valid member

      return (lhs.source_words() == rhs.target_words()) && (lhs.target_words() == rhs.target_words());


      std::vector<std::wstring> extract_words(const std::wstring& line)

      std::wistringstream ifs line ;
      std::vector<std::wstring> ret;
      for (std::wstring s; ifs >> s;)
      ret.push_back(s);
      return ret;;


      std::wstring words_to_line(const std::vector<std::wstring>& sv)
      //makes a vector of strings to a line seperated by whitespace

      std::wstring ret;

      for (const auto&x : sv)
      ret = ret + x + L' ';
      if (!ret.empty()) // remove last ' '
      ret.pop_back();
      return ret;




      main.cpp



      #include <iostream>
      #include <string>

      #include "Menu.h"


      const std::string filename_settings = "settings.txt";
      const std::string filename_vocabulary = "vocsafe.txt";

      int main()
      try
      voc::Menu menu(filename_settings, filename_vocabulary);

      catch (std::runtime_error& e)
      std::wcerr << e.what() << "n";
      utility::keep_window_open(L"~");

      catch (...)
      std::wcerr << "unknown error " << "n";
      utility::keep_window_open(L"~");







      share|improve this question













      I wrote a console based Vocabulary Trainer to practice C++.



      Its possible to add and remove vocabulary and practice it with it.



      I save and read the vocabulary from a file to be able to read it.



      Also certain settings like target and source language color of the console get saved in a settings file.



      I know currently it is not portable because I use Windows functions.



      I tested it on a Mexican Windows 10 system and a German Windows 7 system.



      I wonder if I'm handling the widestrings correctly. During the development I got into the problem that special signs like öäüß don't get displayed properly, so I switched to widestrings in the program. I wonder if my approach is right for this.



      Also, I wonder if it's a good idea to make classes for the Menus.



      In the next step I want to make this a GUI application to practice making GUIs, so I think it's a good time to review the code. Feel free to comment on any part you like.



      File_Vocabulary.h



      #ifndef FILE_VOCABULARY_GUARD100420181849
      #define FILE_VOCABULARY_GUARD100420181849

      #pragma once

      #include <fstream>
      #include <sstream>
      #include <cstdio>
      #include <fcntl.h>
      #include <io.h>

      #include "Vocabulary.h"

      namespace voc

      class File_Vocabulary
      public:
      File_Vocabulary(const std::string& in_filename);

      void add(const Vocabulary& v);
      bool erase(const std::wstring& s); // erase by word
      bool erase(const int row); // erasse by row
      bool is_match(const Vocabulary& v); // checks if vocabulary is exactly like this in file

      int find(const std::vector<std::wstring> s); // checks if words are in file and returns row of it. 0 if no row
      int find(std::wstring s); // checks if words are in file and returns row of it. 0 if no row

      std::vector<Vocabulary> get_part(const int begin, const int end); // return in chuncks specify how many rows of Vocabulary you want
      Vocabulary get_row(const int row); // return row
      int size()const return count;

      size_t max_size_source()const;
      size_t max_size_target()const;

      void reset_learned(); //resets learned to 0 in all vocabulary
      private:
      std::string filename;
      int count; //count of vocabulary;
      ;


      #endif


      File_Vocabulary.cpp



      #include "File_Vocabulary.h"

      namespace voc

      //____________________________Member functions

      File_Vocabulary::File_Vocabulary(const std::string& in_filename)
      :filenamein_filename,count0

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      std::wifstream ifs filename ;
      if (!ifs) return; // file doesnt exist yet

      for (Vocabulary curr; ifs >> curr;)
      ++count;



      void File_Vocabulary::add(const Vocabulary& v)

      const std::string out_filename = "tmp_" + filename;
      //to close the files
      std::wifstream ifs filename ;
      if (ifs) // dont check if file doesnst exist yet

      std::wofstream ofs out_filename ;
      bool added = false;

      for (Vocabulary curr; ifs >> curr;)
      if (curr.source_words() == v.source_words() && !added) //replace definition of vocabulary
      curr.set_target_words(v.target_words());
      curr.set_practiced_right(v.practiced_right());
      added = true;

      else if (curr.source_words() > v.source_words() && !added)
      ofs << v << 'n';
      ++count;
      added = true;

      ofs << curr << 'n';


      if (!added) ofs << v << 'n'; // case file was empty


      else //special case first word to add
      std::wofstream ofs filename ;
      ofs << v << 'n';
      return;


      std::remove(filename.c_str());
      std::rename(out_filename.c_str(), filename.c_str());


      bool File_Vocabulary::erase(const std::wstring& s) //search by word to erase

      const std::string out_filename = "tmp_" + filename;
      bool erased = false;

      std::wifstream ifs filename ;
      if (!ifs) return false;

      std::wofstream ofs out_filename ;

      for (Vocabulary curr; ifs >> curr;)
      if ((curr.source_words() == extract_words(s)


      if (std::remove(filename.c_str()) != 0)
      throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
      "file operation delete old vocabulary failedn");
      if (std::rename(out_filename.c_str(), filename.c_str()) != 0)
      throw std::runtime_error("bool File_Vocabulary::erase(const std::wstring& s)n"
      "file operation rename new to old failedn");

      return erased;


      bool File_Vocabulary::erase(const int row)


      bool File_Vocabulary::is_match(const Vocabulary& v) // checks if vocabulary is exactly like this in file

      std::wifstream ifs filename ;
      if (!ifs) return false;

      for (Vocabulary curr; ifs >> curr;)
      if (v.source_words() == curr.source_words())
      if (v.target_words() == curr.target_words())
      return true;
      else
      return false;


      throw std::runtime_error("bool File_Vocabulary::erase(const int row)nDatabase corrupt. Original word is not in database.n");
      return false;


      int File_Vocabulary::find(const std::vector<std::wstring> s) //checks if word is in file and returns row of it

      std::wifstream ifs filename ;
      if (!ifs) return 0;

      int row = 0;
      for (Vocabulary curr; ifs >> curr;)
      if (curr.source_words() == s
      return -1;


      int File_Vocabulary::find(std::wstring s) //checks if word is in file and returns row of it

      std::wistringstream ist s ;

      std::vector<std::wstring> vs;
      for (std::wstring in; ist >> in;)
      vs.push_back(in);

      return find(vs); // call the other find routine


      std::vector<Vocabulary> File_Vocabulary::get_part(const int begin, const int end) //return in chuncks specify how many rows of Vocabulary you want


      Vocabulary File_Vocabulary::get_row(const int row) // return row

      std::vector<Vocabulary> ret = get_part(row, row);
      return *ret.begin();


      void File_Vocabulary::reset_learned()

      std::wifstream ifs filename ;
      if (!ifs) return;

      const std::string out_filename = "tmp_" + filename;
      std::wofstream ofs out_filename ;

      for (Vocabulary curr; ifs >> curr;)
      curr.set_practiced_right(0);
      ofs << curr << 'n';


      std::remove(filename.c_str());
      std::rename(out_filename.c_str(), filename.c_str());


      size_t File_Vocabulary::max_size_source() const

      std::wifstream ifs filename ;
      if (!ifs) return 0;

      size_t ret = 0;
      for (Vocabulary curr; ifs >> curr;)
      if (curr.source_words().size() > ret)
      ret = curr.source_words().size();

      return ret;


      size_t File_Vocabulary::max_size_target() const

      std::wifstream ifs filename ;
      if (!ifs) return 0;

      size_t ret = 0;
      for (Vocabulary curr; ifs >> curr;)
      if (curr.target_words().size() > ret)
      ret = curr.target_words().size();

      return ret;




      Menu.h



      #ifndef MENU_GUARD110420181806
      #define MENU_GUARD110420181806

      #pragma once

      #include <iostream>
      #include <sstream>
      #include <fcntl.h>
      #include <io.h>

      #include "Menu_settings.h"
      #include "Menu_show.h"

      namespace voc
      class Menu
      public:
      Menu(const std::string& file_settings, const std::string& file_vocabulary);
      private:
      void init_settings();
      void menu_main();
      void menu_practice();
      void menu_add();

      File_Vocabulary file_voc;
      Settings settings;
      std::string fname_settings;
      std::string fname_vocabulary;
      ;


      #endif


      Menu.cpp



      #include"Menu.h"

      namespace voc
      //____________________________Member functions
      Menu::Menu(const std::string& file_settings, const std::string& file_vocabulary)
      :settings 10, L"spanish", L"german", 0, 15 ,
      file_voc file_vocabulary ,
      fname_settingsfile_settings,
      fname_vocabularyfile_vocabulary

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      init_settings();
      menu_main();


      void Menu::init_settings()
      // try to read settings from file
      // if not take default values and write to new file
      // change color from settings

      if (!read_settings_from_file(fname_settings, settings))
      write_settings_to_file(fname_settings, settings);

      change_color(settings.color_background(), settings.color_font());


      void Menu::menu_main()

      enum class Menu_main_choice
      practice = 1,
      add = 2,
      show = 3,
      settings = 4,
      exit = 5
      ;

      while (true)
      Menu_main_choice choice = static_cast<Menu_main_choice>(get_int(1, 5,
      bissmann_print +
      L"n Enter a number to choose an option:"
      "n [1] Practice vocabulary"
      "n [2] Add new vocabulary to database"
      "n [3] Show all words currently in the database. Delete words in database"
      "n [4] Settings"
      "n [5] End program"
      "n"));

      switch (choice)
      case Menu_main_choice::practice: menu_practice();break;
      case Menu_main_choice::add: menu_add(); break;
      case Menu_main_choice::show:
      Menu_show ms fname_settings,settings ,file_voc ;
      break;

      case Menu_main_choice::settings:
      Menu_settings ms fname_settings,settings ,file_voc ;
      settings = ms.get_settings();
      break;

      case Menu_main_choice::exit: return;




      void Menu::menu_practice()

      clear_screen();
      int count_of_rows = file_voc.size()-1;
      if (count_of_rows == 0) // file empty fill first with vocabulary
      std::wcout << "n Error: Database is empty. Fill database first with vocabularyn";
      utility::keep_window_open(L"q");
      return;


      int amount = get_int(1, 1000,bissmann_print +L"n Enter how many words you want to practice.Range 1 - 1000 n");

      Vocabulary rnd_voc; // vocabulary
      int correct_words = 0;
      for (int i = 1; i <= amount; ++i)
      clear_screen();
      std::wcout <<bissmann_print;

      int repeat_rnd = 0;

      do
      rnd_voc = file_voc.get_row(utility::get_random(1, count_of_rows));
      ++repeat_rnd;
      if (repeat_rnd == 1000) //assuming after 1000 rolls no valid word could be found so all are "learned"
      std::wcout << "n You learned all vocabulary by repeating each word " << settings.threshold_learned() << " times correct n";
      utility::keep_window_open(L"q");
      return;

      while (rnd_voc.practiced_right() >= settings.threshold_learned());

      clear_screen();
      std::wcout << bissmann_print
      << "n Word nummber " << i << " out of " << amount << ":n"
      << "n The " << settings.name_of_source_language() << " word is:nn " << rnd_voc.source() << "n"
      << "n Enter the translation in " << settings.name_of_target_language() << ":nn";

      std::wcin.ignore();
      std::vector<std::wstring> input_words = get_words();

      if (input_words == rnd_voc.target_words()) //word translated right
      ++correct_words;

      int practiced_right = rnd_voc.practiced_right();
      ++practiced_right;
      rnd_voc.set_practiced_right(practiced_right);

      std::wcout << "n That is correct!!!n";

      if (rnd_voc.practiced_right() >= settings.threshold_learned())
      std::wcout << "n You learned the word. You answered it correct " << rnd_voc.practiced_right() << " timesn";

      else //translated wrong
      rnd_voc.set_practiced_right(0);
      std::wcout << "n That is wrong !!!"
      << "n The correct translation is: " << rnd_voc.target()<<'n';

      utility::keep_window_open(L"q");
      file_voc.add(rnd_voc);

      clear_screen();
      std::wcout << bissmann_print
      << "n You translatet " << correct_words << " out of " << amount << " words correctly to " << settings.name_of_target_language()<<'n';
      utility::keep_window_open(L"q");
      return;


      void Menu::menu_add()

      while (true)
      clear_screen();
      int choice = get_int(1, 2,
      bissmann_print +
      L"n [1] Add a new word to the database"
      "n [2] Return to main menue"
      "n");
      if (choice == 2) return;

      clear_screen();
      std::wcout << bissmann_print << "n Enter the " << settings.name_of_source_language() << " wordn";

      Vocabulary add_voc;
      std::wcin.ignore();
      std::vector<std::wstring> org_words = get_words();
      add_voc.set_source_words(org_words);

      clear_screen();
      std::wcout << bissmann_print << "n Enter the translation for '" << add_voc.source() << "' in " << settings.name_of_target_language() << 'n';

      std::vector<std::wstring> tar_words = get_words();
      add_voc.set_target_words(tar_words);

      file_voc.add(add_voc);






      Menu_settings.h



      #ifndef MENU_SETTINGS_GUARD160420182054
      #define MENU_SETTINGS_GUARD160420182054

      #pragma once

      #include <string>
      #include <iostream>
      #include <sstream>
      #include <fcntl.h>
      #include <io.h>

      #include "Settings.h"
      #include "misc.h"
      #include "File_Vocabulary.h"

      namespace voc
      class Menu_settings
      public:
      Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
      Settings get_settings() const return settings;
      private:
      void menu_change_color();
      void menu_set_learned();
      void menu_reset_learned();
      void menu_set_languages();

      std::string filename_settings;
      Settings settings;
      File_Vocabulary file_voc;
      ;



      #endif


      Menu_settings.cpp



      #include "Menu_settings.h"

      namespace voc

      Menu_settings::Menu_settings(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
      :filename_settingsfile_settings,settingsset, file_vocfv

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      enum class Menu_settings_choice
      change_color = 1,
      set_learned = 2,
      reset_learned = 3,
      set_languges = 4,
      exit = 5
      ;

      while (true)
      Menu_settings_choice choice = static_cast<Menu_settings_choice>(get_int(1, 5,
      bissmann_print +
      L"n Enter a number to choose an option:"
      "n [1] Change color of the background or the letters"
      "n [2] Define how often to repeat a word until it counts as learned"
      "n [3] Set all vocabulary to not learned"
      "n [4] Define target and source language"
      "n [5] Return to main menue"
      "n"));

      switch (choice)
      case Menu_settings_choice::change_color: menu_change_color(); break;
      case Menu_settings_choice::set_learned: menu_set_learned(); break;
      case Menu_settings_choice::reset_learned: menu_reset_learned(); break;
      case Menu_settings_choice::set_languges: menu_set_languages(); break;
      case Menu_settings_choice::exit: return;




      void Menu_settings::menu_change_color()


      void Menu_settings::menu_set_learned()

      std::wostringstream ost;
      ost << "n Currently each vocabulary has to be translated right"
      "n " << settings.threshold_learned() << " times in a row to make it count as learned."
      "n Enter a new value for practiced right. Range: 1 - 99n";

      settings.set_threshold_learned(get_int(1, 99, bissmann_print + ost.str()));
      write_settings_to_file(filename_settings, settings);


      void Menu_settings::menu_reset_learned()

      int choice = get_int(1, 2,
      bissmann_print +
      L"n Choose an option:"
      "n [1] Reset all words to not learned. Practice right = 0"
      "n [2] Return to settingsn");

      if (choice == 2) return;

      choice = get_int(1, 2,
      bissmann_print +
      L"n Are you SUPER SUPER SURE??"
      "n [1] Reset all words to not learned. Practice right = 0"
      "n [2] Return to settingsn");

      if (choice == 2) return;

      file_voc.reset_learned();
      std::wcout<<"n All words changed to not learnedn";
      utility::keep_window_open(L"q");


      void Menu_settings::menu_set_languages()

      enum class Menu_change_language_choice
      change_source_language = 1,
      change_target_language = 2,
      return_to_settings = 3
      ;

      std::wostringstream ost;
      ost<< "n Currently all words need to be translated from "<<settings.name_of_source_language()<<" to "<< settings.name_of_target_language()<<
      "n Choose an option to change the language"
      "n [1] Change the source language ("<< settings.name_of_source_language() <<")"
      "n [2] Change the target language ("<< settings.name_of_target_language()<<")"
      "n [3] Return to settingsn";

      Menu_change_language_choice choice = static_cast<Menu_change_language_choice>(get_int(1, 3,bissmann_print + ost.str()));

      if (choice == Menu_change_language_choice::return_to_settings) return;
      std::cin.ignore();

      switch (choice)
      case Menu_change_language_choice::change_source_language:
      std::wcout << "Enter the new source language:n";
      std::wstring in;
      getline(std::wcin,in);

      if (in.empty())
      std::wcerr << "Invalid empty string enteredn";
      utility::keep_window_open(L"q");
      return;


      settings.set_name_of_source_language(in);
      write_settings_to_file(filename_settings, settings);
      std::wcout<<"n New source language saved to settings";
      utility::keep_window_open(L"q");
      break;

      case Menu_change_language_choice::change_target_language:
      std::wcout << "Enter the new target language:n";
      std::wstring in;
      getline(std::wcin, in);

      if (in.empty())
      std::wcerr << "Invalid empty string enteredn";
      utility::keep_window_open(L"q");
      return;


      settings.set_name_of_target_language(in);
      write_settings_to_file(filename_settings, settings);
      std::cout<<"n New target language saved to settings";
      utility::keep_window_open(L"q");
      break;

      case Menu_change_language_choice::return_to_settings:
      return;





      Menu_show.h



      #ifndef MENU_SHOW_GUARD170420182020
      #define MENU_SHOW_GUARD170420182020

      #pragma once

      #include <string>
      #include <iostream>
      #include <sstream>
      #include <fcntl.h>
      #include <io.h>
      #include <iomanip>

      #include "Settings.h"
      #include "misc.h"
      #include "File_Vocabulary.h"

      namespace voc
      class Menu_show
      public:
      Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv);
      private:
      enum class Menu_show_choice
      start,
      search,
      erase,
      exit
      ;

      void menu_search();
      void menu_erase();

      std::string filename_settings;
      Settings settings;
      File_Vocabulary file_voc;
      Menu_show_choice step;
      int delete_row;

      static constexpr int rows_per_page = 15;
      static constexpr char left_arrow = 75;
      static constexpr char right_arrow = 77;

      static constexpr int max_len_of_display = 35; // complete window = 80
      ;



      #endif


      Menu_show.cpp



      #include "Menu_show.h"

      namespace voc

      Menu_show::Menu_show(const std::string& file_settings, const Settings& set, const File_Vocabulary& fv)
      :filename_settings file_settings , settings set , file_voc fv ,stepMenu_show_choice::start,delete_row0

      _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
      _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin

      int page = 1;
      while (true)
      if (file_voc.size() < 1)
      clear_screen();
      std::wcout << "n Database is emptyn";
      utility::keep_window_open(L"q");
      return;



      int count_of_pages = file_voc.size() / rows_per_page;
      if ((file_voc.size() % rows_per_page) != 0) //if fracture there must be one more page
      ++count_of_pages;

      clear_screen();
      std::wcout << "row ";
      std::wcout << settings.name_of_source_language() << ':';

      // check what is possible max len and then break to the next line
      for (size_t i = 0; i < max_len_of_display - settings.name_of_source_language().size(); ++i) //max len = longest word in database !!!
      std::wcout << ' ';

      std::wcout << settings.name_of_target_language() << ':';

      for (size_t i = 0; i < max_len_of_display - settings.name_of_target_language().size(); ++i) //max len = longest word in database !!!
      std::wcout << ' ';

      std::wcout << "OK ";

      for (int i = 0; i < 80; ++i)
      std::wcout << '-';

      int start_row = 0 + (page - 1) * rows_per_page;
      int end_row = 0;
      if (page == count_of_pages)
      end_row = file_voc.size()-1;
      else
      end_row = start_row + rows_per_page - 1;

      //read vocabulary from file in chunks
      std::vector<Vocabulary> print_voc file_voc.get_part(start_row, end_row) ;

      for (size_t i=0; i<print_voc.size(); ++i)
      std::wcout << std::setw(4) << std::left << start_row+1 + i
      << "
      for (int i = 0; i < 80; ++i)
      std::wcout << '-';

      if (page != 1) std::wcout << "<-";
      else std::wcout << " ";
      if (page != count_of_pages) std::wcout << " ->";
      else std::wcout << " ";
      std::wcout << " page " << page << " of " << count_of_pages;

      switch (step)
      case Menu_show_choice::exit:
      return;
      case Menu_show_choice::start:
      std::wcout <<
      "n Choose an option:"
      "n [1] Search for a word to delete it"
      "n [2] Return to main menue"
      "n";
      int choice = read_single_char();
      if (choice == '1')
      step = Menu_show_choice::search;
      else if (choice == '2') // Back to main Menue
      return;
      else
      choice = read_single_char(); //Call twice because first time return always 0xE0 second returns code
      step = Menu_show_choice::start;

      if (choice == left_arrow && page != 1)
      --page;
      if (choice == right_arrow && page != count_of_pages)
      ++page;
      break;

      case Menu_show_choice::search: menu_search();
      break;
      case Menu_show_choice::erase: menu_erase();
      break;




      void Menu_show::menu_search() //ugly better with a class

      std::wcout << "n Enter a word or the specific row to searchn a entry in the vocabulary database:n";
      std::wstring input;
      std::wcin.ignore();
      std::getline(std::wcin, input);

      bool is_number = true;

      try
      delete_row = std::stoi(input);
      --delete_row;//because humans start on 1 not on 0

      catch (...) // catch stoi std::invalid_argument std::out_of_range
      is_number = false;


      if (!is_number) //search by name
      delete_row = file_voc.find(input); // ??? delete row == 0

      if (delete_row == 0) //No match
      std::wcout << "n No match found in filen";
      utility::keep_window_open(L"q");
      step = Menu_show_choice::start; // break and set global menu
      return;


      else // search by row number
      if (delete_row < 0
      step = Menu_show_choice::erase; // go to next step erase


      void Menu_show::menu_erase()

      Vocabulary delete_voc;
      delete_voc = file_voc.get_row(delete_row); // get word to delte for display

      int choice = 0;
      bool repeat = false;

      for (int i = 0; i <2; ++i) "
      << std::setw(max_len_of_display) << std::left << delete_voc.target().substr(0, max_len_of_display)
      << "
      file_voc.erase(delete_row);
      std::wcout << "n Hasta la vista baby!!!!!"
      << "n Entry has been terminated.n";
      utility::keep_window_open(L"q");

      step = Menu_show_choice::start;




      misc.h



      #ifndef MISC_GUARD120420181946
      #define MISC_GUARD120420181946

      #pragma once

      #include <string>
      #include <sstream>
      #include <map>
      #include <cctype>
      #include <cwctype>
      #include <iostream>
      #include <conio.h>
      #include <Windows.h>

      #include "Utility.h"

      namespace voc

      const std::wstring bissmann_print =

      L"n--------Bißmann's-------"
      "n---Vocabulary Trainer---"
      "n---------V3.0-----------"
      "n"
      ;

      void clear_screen(); //Windows only not portable
      int read_single_char(void);
      void change_color(char background, char font); //Windows only not portable
      std::wstring hex_to_color(int hex);
      char hex_to_ascii(char c);

      // functions to safely read in an integer
      void skip_to_int();
      int get_int();
      int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry = L"");

      std::vector<std::wstring> get_words();


      #endif


      misc.cpp



      #include "misc.h"

      namespace voc

      void clear_screen()

      system("cls"); //WINDOWS ONLY


      int read_single_char(void)

      return _getch(); //WINDOWS ONLY


      void change_color(char background, char font)

      std::string color = "COLOR ";
      color.push_back(hex_to_ascii(background));
      color.push_back(hex_to_ascii(font));
      system(color.c_str()); //WINDOWS ONLY


      std::wstring hex_to_color(int hex)

      const std::vector<std::wstring> color_hex =

      L"Black",
      L"Blue",
      L"Green",
      L"Aqua",
      L"Red",
      L"Purple",
      L"Yellow",
      L"White",
      L"Gray",
      L"Light Blue",
      L"Light Green",
      L"Light Aqua",
      L"Light Red",
      L"Light Purple",
      L"Light Yellow",
      L"Bright White"
      ;

      return color_hex[hex];


      char hex_to_ascii(char c)

      if (c < 10) // convert number from hex to ascii. different offsets for 1-9 and a-z in ascii file
      c += 48;
      else
      c += 55;
      return c;


      void skip_to_int()

      if (std::wcin.fail()) // we found sth that wasnt an integer
      std::wcin.clear(); // wed like to look at the characters
      for (wchar_t ch; std::wcin >> ch;) ch == '-')
      std::wcin.unget(); //put the digit back so that we can read the number
      return;






      int get_int()

      int n = 0;
      while (true)
      if (std::wcin >> n) return n;
      skip_to_int();



      int get_int(int low, int high, const std::wstring& greeting, const std::wstring& sorry)

      while (true)
      clear_screen();
      std::wcout << greeting;

      int n = get_int();
      if (low <= n && n <= high) return n;
      if (!sorry.empty())
      std::wcout << sorry;
      utility::keep_window_open();




      std::vector<std::wstring> get_words()
      //reads in line and returns the individual words of it

      std::wstring line;
      std::getline(std::wcin, line);

      std::wistringstream iss line ;
      std::vector<std::wstring> ret;

      for (std::wstring in; iss >> in;)
      ret.push_back(in);
      return ret;




      settings.h



      #ifndef SETTINGS_GUARD110420181833
      #define SETTINGS_GUARD110420181833

      #pragma once

      #include <string>
      #include <fstream>
      #include <fcntl.h>
      #include <io.h>

      namespace voc

      class Settings
      public:
      Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font);

      void set_threshold_learned(int threshold) thr_learnd = threshold;
      void set_name_of_source_language(const std::wstring& source_name) s_language = source_name;
      void set_name_of_target_language(const std::wstring& target_name) t_language = target_name;
      void set_color_background(int back) clr_back = back;
      void set_color_font(int font) clr_font = font;

      int threshold_learned() const return thr_learnd; ;
      std::wstring name_of_source_language() const return s_language;
      std::wstring name_of_target_language() const return t_language;
      char color_background() const return clr_back;
      char color_font() const return clr_font;
      private:
      int thr_learnd; //learned
      std::wstring s_language;
      std::wstring t_language;
      char clr_back;
      char clr_font;
      ;

      std::wostream& operator<<(std::wostream& os, const Settings& obj);
      std::wistream& operator>>(std::wistream& is, Settings& obj);

      void write_settings_to_file(const std::string& filename, Settings& data);
      bool read_settings_from_file(const std::string& filename, Settings& data);


      #endif


      Settings.cpp



      #include "Settings.h"

      namespace voc
      Settings::Settings(int threshold, const std::wstring& source_name, const std::wstring& target_name, char color_back, char color_font)
      :thr_learnd threshold ,
      s_language source_name ,
      t_language target_name ,
      clr_back color_back ,
      clr_font color_font
      color_back > 15

      //____________________________Helper functions
      std::wostream& operator<<(std::wostream& os, const Settings& obj)


      os << obj.threshold_learned() << 't'
      << obj.name_of_source_language() << 't'
      << obj.name_of_target_language() << 't'
      << static_cast<int>(obj.color_background()) << 't'
      << static_cast<int>(obj.color_font());

      return os;

      std::wistream& operator>>(std::wistream& is, Settings& obj)

      int learned;
      std::wstring source;
      std::wstring target;
      int back;
      int font;

      is >> learned >> source >> target >> back >> font;

      Settings ret(learned, source, target, static_cast<char>(back), static_cast<char>(font));
      obj = ret;

      return is;


      void write_settings_to_file(const std::string& filename, Settings& data)

      std::wofstream ofs filename ;
      ofs << data;



      bool read_settings_from_file(const std::string& filename, Settings& data)

      std::wifstream ifs filename ;
      if (!ifs) return false;

      ifs >> data;
      return true;




      Utility.h



      #ifndef UTILITY_GUARD11042018
      #define UTILITY_GUARD11042018

      #pragma once

      #include <iostream>
      #include <string>
      #include <random>
      #include <chrono>

      namespace utility

      inline void keep_window_open(std::wstring s)

      if (s == L"") return;
      std::wcin.clear();
      std::wcin.ignore(120, 'n');
      for (;;)
      std::wcout << "Please enter " << s << " to exitn";
      std::wstring ss;
      while (std::wcin >> ss && ss != s)
      std::wcout << "Please enter " << s << " to exitn";
      return;



      inline void keep_window_open()

      std::wcin.clear();
      std::wcout << "Please enter a character to exitn";
      wchar_t ch;
      std::wcin >> ch;
      return;


      int get_random(int min, int max);


      #endif


      Utility.cpp



      #include "Utility.h"


      namespace utility

      int get_random(int min, int max)

      std::random_device rd;
      std::mt19937 mt(rd());
      std::uniform_int_distribution<int> distribution(min, max);
      return distribution(mt);




      Vocabulary.h



      #ifndef VOCABULARY_GUARD100420181927
      #define VOCABULARY_GUARD100420181927

      #pragma once

      #include <string>
      #include <vector>
      #include <sstream>

      namespace voc

      class Vocabulary
      public:
      Vocabulary();
      Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar);
      Vocabulary(const std::wstring& org, const std::wstring& tar);

      std::vector<std::wstring> source_words()const return src_words;
      std::vector<std::wstring> target_words()const return tar_words;

      std::wstring source()const;
      std::wstring target()const;

      int practiced_right() const return pr;

      void set_source_words(const std::vector<std::wstring>& org) src_words = org;
      void set_target_words(const std::vector<std::wstring>& tar) tar_words = tar;
      void set_practiced_right(int p) pr = p;
      private:
      std::vector<std::wstring> src_words;
      std::vector<std::wstring> tar_words;
      int pr; //practiced_right
      ;

      std::wostream& operator<<(std::wostream& os, const Vocabulary& obj);
      std::wistream& operator>>(std::wistream& is, Vocabulary& obj);
      inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs);

      std::vector<std::wstring> extract_words(const std::wstring& line);
      std::wstring words_to_line(const std::vector<std::wstring>& sv);


      #endif


      Vocabulary.cpp



      #include "Vocabulary.h"

      namespace voc
      //____________________________Member functions
      Vocabulary::Vocabulary()
      :pr0



      Vocabulary::Vocabulary(const std::vector<std::wstring>& org, const std::vector<std::wstring>& tar)
      :src_wordsorg,tar_wordstar,pr0



      Vocabulary::Vocabulary(const std::wstring& org, const std::wstring& tar)
      : src_words extract_words(org) ,tar_words extract_words(tar) ,pr0



      std::wstring Vocabulary::source()const return words_to_line(src_words);
      std::wstring Vocabulary::target()const return words_to_line(tar_words);

      //____________________________Helper functions
      std::wostream& operator<<(std::wostream& os, const Vocabulary& obj)

      for (const auto& x : obj.source_words())
      os << x + L" ";
      os << "# ";
      for (const auto& x : obj.target_words())
      os << x + L" ";
      os << "# ";
      os << obj.practiced_right();

      return os;

      std::wistream& operator>>(std::wistream& is, Vocabulary& obj)

      std::wstring line;
      std::getline(is,line);
      std::wistringstream ifs line ;

      std::vector<std::wstring> org;
      bool first = false;
      for (std::wstring s; ifs >> s;)
      if (s == L"#")
      first = true;
      break;

      org.push_back(s);


      if (!first) // end of stream but first # not found
      is.setstate(std::ios::failbit);
      return is;


      std::vector<std::wstring> tar;
      bool second = false;
      for (std::wstring s; ifs >> s;)
      if (s == L"#")
      second = true;
      break;

      tar.push_back(s);


      if (!second) // same for second
      is.setstate(std::ios::failbit);
      return is;


      std::wstring s;
      ifs >> s;
      obj.set_source_words(org);
      obj.set_target_words(tar);
      obj.set_practiced_right(std::stoi(s));

      return is;


      inline bool operator==(const Vocabulary& lhs, const Vocabulary& rhs)
      // no check of valid member

      return (lhs.source_words() == rhs.target_words()) && (lhs.target_words() == rhs.target_words());


      std::vector<std::wstring> extract_words(const std::wstring& line)

      std::wistringstream ifs line ;
      std::vector<std::wstring> ret;
      for (std::wstring s; ifs >> s;)
      ret.push_back(s);
      return ret;;


      std::wstring words_to_line(const std::vector<std::wstring>& sv)
      //makes a vector of strings to a line seperated by whitespace

      std::wstring ret;

      for (const auto&x : sv)
      ret = ret + x + L' ';
      if (!ret.empty()) // remove last ' '
      ret.pop_back();
      return ret;




      main.cpp



      #include <iostream>
      #include <string>

      #include "Menu.h"


      const std::string filename_settings = "settings.txt";
      const std::string filename_vocabulary = "vocsafe.txt";

      int main()
      try
      voc::Menu menu(filename_settings, filename_vocabulary);

      catch (std::runtime_error& e)
      std::wcerr << e.what() << "n";
      utility::keep_window_open(L"~");

      catch (...)
      std::wcerr << "unknown error " << "n";
      utility::keep_window_open(L"~");









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jun 21 at 21:18









      200_success

      123k14143399




      123k14143399









      asked Jun 21 at 19:59









      Sandro4912

      467119




      467119




















          3 Answers
          3






          active

          oldest

          votes

















          up vote
          3
          down vote



          accepted










          Here are my remarks, without any further structure or grouping. I started reading the code top to bottom, but then started to jump around.



          #ifndef FILE_VOCABULARY_GUARD100420181849
          #define FILE_VOCABULARY_GUARD100420181849

          #pragma once


          Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.



          I don't understand the name File_Vocabulary. I'd rather call this
          VocabularyFile. Combining the underscore with PascalCase is not typical for C++, it looks more like Ada.



          The class VocabularyFile does too much, in my opinion. It should only have two methods at all: save and load. All the other operations can be done in-memory instead. Since this program is handling vocabulary, there will not be more than a million entries in a single vocabulary file. (No human being could ever remember this many vocabularies.) Therefore you will probably not run into an out-of-memory situation.



          What is the purpose of max_size_source and max_size_target? They are defined but never used. Your IDE should tell you this.



          You should start the presentation of the code with Vocabulary.h and Vocabulary.cpp, since these are the most important data structures in the program. Without reading these files first, it is difficult to understand File_Vocabulary.cpp.



          In Vocabulary.h (and many other places), you should name the variables properly. org and tar are not useful, since tar is the well-known Tape Archiver. Better name these variables source and target. Oh, I see, you did that for the source_words and target_words methods. In that case, I change my advice to "Be consistent in naming things". Since methods must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense. Then you have source_words() and m_source_words, and the reader doesn't have to guess anymore.



          In Vocabulary.cpp, the operator>> looks misplaced to me. It clearly belongs to the other code that is concerned about the file format. By reading the code alone, the file format is not easy to guess. Therefore you should have included an example vocabulary file along with the code.



          In Vocabulary.cpp, in method operator<<, you should replace os << x + L" " with os << x << L" ", to prevent unnecessary string allocation.



          The variable names in words_to_line are bad. Rename sv to words and ret to line and x to word. This will make the code intuitive to read:



          std::wstring words_to_line(const std::vector<std::wstring>& words)

          std::wstring line;

          for (const auto& word : words)
          line = line + word + L' ';
          if (!line.empty()) // remove last ' '
          line.pop_back();
          return line;



          Switching to Utility.h, function keep_window_open.



          The parameter s should better be named exit_command. Its type should be const&, since it is not modified in the function. Instead of std::wcin >> ss, you should rather use std::getline(std::wcin, ss), because otherwise pressing Enter does not provide a good user experience. Oh, and rename ss to line, to make the code clearer.



          Since the string "Please enter" is duplicated, you could save it into a variable at the beginning of the function:



          void keep_window_open(const std::wstring& exit_command)

          if (s == L"") return;
          std::wcin.clear();
          std::wcin.ignore(120, 'n');

          std::wstring prompt = "Please enter " + s + " to exitn";
          while (true)
          std::wcout << prompt;
          while (std::getline(std::wcin, line) && ...




          Wait … the for (;;) or while (true) is completely useless. As is the whole function. Why should the user enter a specific word to close the window? Just let them press Enter once. The whole function should be removed and replaced with the parameterless keep_window_open below it.



          By the way, the inline is not useful for functions doing interactive I/O, and the return at the end is redundant. You can just remove both.



          That's it for now. I think it is already enough to improve your code by a fair amount.






          share|improve this answer





















          • ⟪Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.⟫ Using both is OK, since the slower #ifdef is a fallback if the fast #pragma is not supported. But, since this is not portable anyway, might as well count on the #pragma.
            – JDługosz
            Jun 22 at 8:37










          • ⟪Since methods [sic] must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense.⟫ It has become common place, not common sense. Others will say that all such tags are to be avoided because the algorithm should not care where the value comes from, and in fact they tend to migrate as code is made more general.
            – JDługosz
            Jun 22 at 8:40

















          up vote
          4
          down vote














          Just some basic stuff, will add more later.




          Strings



          The implementation uses a lot of hard-coded strings and is inconsistent regarding their encodings. Some strings are "normal" ASCII ones whereas others are wide strings. This might cause a lot of unforeseen problems.



          Randomness



          utility::get_random might actually always return the same result for the same inputs. Depending on the standard library implementation, std::random_device might return the same sequence for each created object.



          Since utility::get_random always creates a new object this basically "resets" the sequence for those implementations. (It's conceptually equivalent to srand(0); /* do something with */ rand();).



          This can be fixed by marking rd as static so it only gets instantiated once. (And while we're at it, it might make sense to make mt static as well, since its construction is quite costly).



          Variable naming



          Many variables have very short and very cryptic names. This makes reasoning about code a lot harder than it has to be. Trying to understand the code gets interrupted by questions like "What is the difference between s and ss?", where a better choice of names would make their usage obvious.



          Magic numbers/strings



          There are very many hard-coded strings and numbers, and not always is it obvious what they are meant for.



          Example:



          settings 10, L"spanish", L"german", 0, 15 // taken from the menu constructor


          It isn't obvious at all what all those values represent. Compare this with:



          settings threshold, source_lang, target_lang, colors::black, colors::white 


          With this, I as a reader of the code don't have to guess what all those values mean.



          Other stuff



          • Nearly all the work is done in the menu constructor. This makes it very hard to reuse or even test that class.





          share|improve this answer























          • i guess it would be better to split the menues in smaller classes? do you have a suggestion?
            – Sandro4912
            Jun 23 at 9:48

















          up vote
          2
          down vote













          int find(std::wstring s);


          Why is this passed by value (making a copy)? And I find it odd that “checks if words are in file and returns row of it” would also modify the file object!



          So, should this really be



          rownum_t find (const std::wstring& s) const;


          ? Note also I changed the return type to indicate its actual domain. Even if you just make that a typedef with no compile-time checking, it still is good human documentation.



          std::vector<Vocabulary> get_part(const int begin, const int end);


          Good that you’re returning the vector by value as a normal return value — many people try to avoid that for outdated reasons. But, again you did not make the function itself const, so you are implying that calling this will modify the file object.



          Second, the const in the parameters don’t have meaning there. It is useful in a function definition, but ignored in a declaration so omitted as not being part of the interface contract.



          As far as the use of wide strings (note for others: On Windows wchar_t is 16 bits), it is necessary for calling Win32 API functions. But for most uses in the program, it is easier to just use utf8 everywhere.



          Note that C++ supports (portable) UTF8 string literals now, so you can write



          const char word = u8"Füße";


          will show up in the source editor and code UTF-8 bytes in the character array, regardless of what ANSI code page is in effect when you run the compiler.



          For display: if you are outputting to the Windows Console, the normal 8-bit stream will use the current code page. In the past, I’ve had inconsistent results using Microsoft’s library just switching to the wide calls — but that was years ago; maybe it’s fixed now. I got proper output by using the raw Win32 console output commands instead of the stream wrappers that feed standard output to the console.



          Meanwhile, the font loaded in the Console will not display characters outside of the current code page (or closely related ones). You may need to switch to a Unicode font in the Console’s system menu. A simple program to print the alphabet (in each language) will tell you if it is OK to proceed.






          share|improve this answer





















          • does that mean i can live without using wstring in the whole programm?
            – Sandro4912
            Jun 23 at 9:44










          • Use wstring inside your wrapper functions that call the Win32 API xxxxW functions.
            – JDługosz
            Jun 23 at 9:48










          • what do you mean with Win32 API xxxxW functions? Im sorry i cant follow. You mean if i use console and file io in the program?
            – Sandro4912
            Jun 25 at 15:35










          • Windows API functions come in pairs. E.g. you have WriteConsoleA and WriteConsoleW, CreateFileA and CreateFileW. I was referring to the –W forms, which take wchar_t encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to use wchar_t inside the wrappers.
            – JDługosz
            Jun 26 at 18:12










          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f197007%2fconsole-based-vocabulary-trainer%23new-answer', 'question_page');

          );

          Post as a guest






























          3 Answers
          3






          active

          oldest

          votes








          3 Answers
          3






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          3
          down vote



          accepted










          Here are my remarks, without any further structure or grouping. I started reading the code top to bottom, but then started to jump around.



          #ifndef FILE_VOCABULARY_GUARD100420181849
          #define FILE_VOCABULARY_GUARD100420181849

          #pragma once


          Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.



          I don't understand the name File_Vocabulary. I'd rather call this
          VocabularyFile. Combining the underscore with PascalCase is not typical for C++, it looks more like Ada.



          The class VocabularyFile does too much, in my opinion. It should only have two methods at all: save and load. All the other operations can be done in-memory instead. Since this program is handling vocabulary, there will not be more than a million entries in a single vocabulary file. (No human being could ever remember this many vocabularies.) Therefore you will probably not run into an out-of-memory situation.



          What is the purpose of max_size_source and max_size_target? They are defined but never used. Your IDE should tell you this.



          You should start the presentation of the code with Vocabulary.h and Vocabulary.cpp, since these are the most important data structures in the program. Without reading these files first, it is difficult to understand File_Vocabulary.cpp.



          In Vocabulary.h (and many other places), you should name the variables properly. org and tar are not useful, since tar is the well-known Tape Archiver. Better name these variables source and target. Oh, I see, you did that for the source_words and target_words methods. In that case, I change my advice to "Be consistent in naming things". Since methods must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense. Then you have source_words() and m_source_words, and the reader doesn't have to guess anymore.



          In Vocabulary.cpp, the operator>> looks misplaced to me. It clearly belongs to the other code that is concerned about the file format. By reading the code alone, the file format is not easy to guess. Therefore you should have included an example vocabulary file along with the code.



          In Vocabulary.cpp, in method operator<<, you should replace os << x + L" " with os << x << L" ", to prevent unnecessary string allocation.



          The variable names in words_to_line are bad. Rename sv to words and ret to line and x to word. This will make the code intuitive to read:



          std::wstring words_to_line(const std::vector<std::wstring>& words)

          std::wstring line;

          for (const auto& word : words)
          line = line + word + L' ';
          if (!line.empty()) // remove last ' '
          line.pop_back();
          return line;



          Switching to Utility.h, function keep_window_open.



          The parameter s should better be named exit_command. Its type should be const&, since it is not modified in the function. Instead of std::wcin >> ss, you should rather use std::getline(std::wcin, ss), because otherwise pressing Enter does not provide a good user experience. Oh, and rename ss to line, to make the code clearer.



          Since the string "Please enter" is duplicated, you could save it into a variable at the beginning of the function:



          void keep_window_open(const std::wstring& exit_command)

          if (s == L"") return;
          std::wcin.clear();
          std::wcin.ignore(120, 'n');

          std::wstring prompt = "Please enter " + s + " to exitn";
          while (true)
          std::wcout << prompt;
          while (std::getline(std::wcin, line) && ...




          Wait … the for (;;) or while (true) is completely useless. As is the whole function. Why should the user enter a specific word to close the window? Just let them press Enter once. The whole function should be removed and replaced with the parameterless keep_window_open below it.



          By the way, the inline is not useful for functions doing interactive I/O, and the return at the end is redundant. You can just remove both.



          That's it for now. I think it is already enough to improve your code by a fair amount.






          share|improve this answer





















          • ⟪Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.⟫ Using both is OK, since the slower #ifdef is a fallback if the fast #pragma is not supported. But, since this is not portable anyway, might as well count on the #pragma.
            – JDługosz
            Jun 22 at 8:37










          • ⟪Since methods [sic] must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense.⟫ It has become common place, not common sense. Others will say that all such tags are to be avoided because the algorithm should not care where the value comes from, and in fact they tend to migrate as code is made more general.
            – JDługosz
            Jun 22 at 8:40














          up vote
          3
          down vote



          accepted










          Here are my remarks, without any further structure or grouping. I started reading the code top to bottom, but then started to jump around.



          #ifndef FILE_VOCABULARY_GUARD100420181849
          #define FILE_VOCABULARY_GUARD100420181849

          #pragma once


          Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.



          I don't understand the name File_Vocabulary. I'd rather call this
          VocabularyFile. Combining the underscore with PascalCase is not typical for C++, it looks more like Ada.



          The class VocabularyFile does too much, in my opinion. It should only have two methods at all: save and load. All the other operations can be done in-memory instead. Since this program is handling vocabulary, there will not be more than a million entries in a single vocabulary file. (No human being could ever remember this many vocabularies.) Therefore you will probably not run into an out-of-memory situation.



          What is the purpose of max_size_source and max_size_target? They are defined but never used. Your IDE should tell you this.



          You should start the presentation of the code with Vocabulary.h and Vocabulary.cpp, since these are the most important data structures in the program. Without reading these files first, it is difficult to understand File_Vocabulary.cpp.



          In Vocabulary.h (and many other places), you should name the variables properly. org and tar are not useful, since tar is the well-known Tape Archiver. Better name these variables source and target. Oh, I see, you did that for the source_words and target_words methods. In that case, I change my advice to "Be consistent in naming things". Since methods must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense. Then you have source_words() and m_source_words, and the reader doesn't have to guess anymore.



          In Vocabulary.cpp, the operator>> looks misplaced to me. It clearly belongs to the other code that is concerned about the file format. By reading the code alone, the file format is not easy to guess. Therefore you should have included an example vocabulary file along with the code.



          In Vocabulary.cpp, in method operator<<, you should replace os << x + L" " with os << x << L" ", to prevent unnecessary string allocation.



          The variable names in words_to_line are bad. Rename sv to words and ret to line and x to word. This will make the code intuitive to read:



          std::wstring words_to_line(const std::vector<std::wstring>& words)

          std::wstring line;

          for (const auto& word : words)
          line = line + word + L' ';
          if (!line.empty()) // remove last ' '
          line.pop_back();
          return line;



          Switching to Utility.h, function keep_window_open.



          The parameter s should better be named exit_command. Its type should be const&, since it is not modified in the function. Instead of std::wcin >> ss, you should rather use std::getline(std::wcin, ss), because otherwise pressing Enter does not provide a good user experience. Oh, and rename ss to line, to make the code clearer.



          Since the string "Please enter" is duplicated, you could save it into a variable at the beginning of the function:



          void keep_window_open(const std::wstring& exit_command)

          if (s == L"") return;
          std::wcin.clear();
          std::wcin.ignore(120, 'n');

          std::wstring prompt = "Please enter " + s + " to exitn";
          while (true)
          std::wcout << prompt;
          while (std::getline(std::wcin, line) && ...




          Wait … the for (;;) or while (true) is completely useless. As is the whole function. Why should the user enter a specific word to close the window? Just let them press Enter once. The whole function should be removed and replaced with the parameterless keep_window_open below it.



          By the way, the inline is not useful for functions doing interactive I/O, and the return at the end is redundant. You can just remove both.



          That's it for now. I think it is already enough to improve your code by a fair amount.






          share|improve this answer





















          • ⟪Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.⟫ Using both is OK, since the slower #ifdef is a fallback if the fast #pragma is not supported. But, since this is not portable anyway, might as well count on the #pragma.
            – JDługosz
            Jun 22 at 8:37










          • ⟪Since methods [sic] must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense.⟫ It has become common place, not common sense. Others will say that all such tags are to be avoided because the algorithm should not care where the value comes from, and in fact they tend to migrate as code is made more general.
            – JDługosz
            Jun 22 at 8:40












          up vote
          3
          down vote



          accepted







          up vote
          3
          down vote



          accepted






          Here are my remarks, without any further structure or grouping. I started reading the code top to bottom, but then started to jump around.



          #ifndef FILE_VOCABULARY_GUARD100420181849
          #define FILE_VOCABULARY_GUARD100420181849

          #pragma once


          Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.



          I don't understand the name File_Vocabulary. I'd rather call this
          VocabularyFile. Combining the underscore with PascalCase is not typical for C++, it looks more like Ada.



          The class VocabularyFile does too much, in my opinion. It should only have two methods at all: save and load. All the other operations can be done in-memory instead. Since this program is handling vocabulary, there will not be more than a million entries in a single vocabulary file. (No human being could ever remember this many vocabularies.) Therefore you will probably not run into an out-of-memory situation.



          What is the purpose of max_size_source and max_size_target? They are defined but never used. Your IDE should tell you this.



          You should start the presentation of the code with Vocabulary.h and Vocabulary.cpp, since these are the most important data structures in the program. Without reading these files first, it is difficult to understand File_Vocabulary.cpp.



          In Vocabulary.h (and many other places), you should name the variables properly. org and tar are not useful, since tar is the well-known Tape Archiver. Better name these variables source and target. Oh, I see, you did that for the source_words and target_words methods. In that case, I change my advice to "Be consistent in naming things". Since methods must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense. Then you have source_words() and m_source_words, and the reader doesn't have to guess anymore.



          In Vocabulary.cpp, the operator>> looks misplaced to me. It clearly belongs to the other code that is concerned about the file format. By reading the code alone, the file format is not easy to guess. Therefore you should have included an example vocabulary file along with the code.



          In Vocabulary.cpp, in method operator<<, you should replace os << x + L" " with os << x << L" ", to prevent unnecessary string allocation.



          The variable names in words_to_line are bad. Rename sv to words and ret to line and x to word. This will make the code intuitive to read:



          std::wstring words_to_line(const std::vector<std::wstring>& words)

          std::wstring line;

          for (const auto& word : words)
          line = line + word + L' ';
          if (!line.empty()) // remove last ' '
          line.pop_back();
          return line;



          Switching to Utility.h, function keep_window_open.



          The parameter s should better be named exit_command. Its type should be const&, since it is not modified in the function. Instead of std::wcin >> ss, you should rather use std::getline(std::wcin, ss), because otherwise pressing Enter does not provide a good user experience. Oh, and rename ss to line, to make the code clearer.



          Since the string "Please enter" is duplicated, you could save it into a variable at the beginning of the function:



          void keep_window_open(const std::wstring& exit_command)

          if (s == L"") return;
          std::wcin.clear();
          std::wcin.ignore(120, 'n');

          std::wstring prompt = "Please enter " + s + " to exitn";
          while (true)
          std::wcout << prompt;
          while (std::getline(std::wcin, line) && ...




          Wait … the for (;;) or while (true) is completely useless. As is the whole function. Why should the user enter a specific word to close the window? Just let them press Enter once. The whole function should be removed and replaced with the parameterless keep_window_open below it.



          By the way, the inline is not useful for functions doing interactive I/O, and the return at the end is redundant. You can just remove both.



          That's it for now. I think it is already enough to improve your code by a fair amount.






          share|improve this answer













          Here are my remarks, without any further structure or grouping. I started reading the code top to bottom, but then started to jump around.



          #ifndef FILE_VOCABULARY_GUARD100420181849
          #define FILE_VOCABULARY_GUARD100420181849

          #pragma once


          Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.



          I don't understand the name File_Vocabulary. I'd rather call this
          VocabularyFile. Combining the underscore with PascalCase is not typical for C++, it looks more like Ada.



          The class VocabularyFile does too much, in my opinion. It should only have two methods at all: save and load. All the other operations can be done in-memory instead. Since this program is handling vocabulary, there will not be more than a million entries in a single vocabulary file. (No human being could ever remember this many vocabularies.) Therefore you will probably not run into an out-of-memory situation.



          What is the purpose of max_size_source and max_size_target? They are defined but never used. Your IDE should tell you this.



          You should start the presentation of the code with Vocabulary.h and Vocabulary.cpp, since these are the most important data structures in the program. Without reading these files first, it is difficult to understand File_Vocabulary.cpp.



          In Vocabulary.h (and many other places), you should name the variables properly. org and tar are not useful, since tar is the well-known Tape Archiver. Better name these variables source and target. Oh, I see, you did that for the source_words and target_words methods. In that case, I change my advice to "Be consistent in naming things". Since methods must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense. Then you have source_words() and m_source_words, and the reader doesn't have to guess anymore.



          In Vocabulary.cpp, the operator>> looks misplaced to me. It clearly belongs to the other code that is concerned about the file format. By reading the code alone, the file format is not easy to guess. Therefore you should have included an example vocabulary file along with the code.



          In Vocabulary.cpp, in method operator<<, you should replace os << x + L" " with os << x << L" ", to prevent unnecessary string allocation.



          The variable names in words_to_line are bad. Rename sv to words and ret to line and x to word. This will make the code intuitive to read:



          std::wstring words_to_line(const std::vector<std::wstring>& words)

          std::wstring line;

          for (const auto& word : words)
          line = line + word + L' ';
          if (!line.empty()) // remove last ' '
          line.pop_back();
          return line;



          Switching to Utility.h, function keep_window_open.



          The parameter s should better be named exit_command. Its type should be const&, since it is not modified in the function. Instead of std::wcin >> ss, you should rather use std::getline(std::wcin, ss), because otherwise pressing Enter does not provide a good user experience. Oh, and rename ss to line, to make the code clearer.



          Since the string "Please enter" is duplicated, you could save it into a variable at the beginning of the function:



          void keep_window_open(const std::wstring& exit_command)

          if (s == L"") return;
          std::wcin.clear();
          std::wcin.ignore(120, 'n');

          std::wstring prompt = "Please enter " + s + " to exitn";
          while (true)
          std::wcout << prompt;
          while (std::getline(std::wcin, line) && ...




          Wait … the for (;;) or while (true) is completely useless. As is the whole function. Why should the user enter a specific word to close the window? Just let them press Enter once. The whole function should be removed and replaced with the parameterless keep_window_open below it.



          By the way, the inline is not useful for functions doing interactive I/O, and the return at the end is redundant. You can just remove both.



          That's it for now. I think it is already enough to improve your code by a fair amount.







          share|improve this answer













          share|improve this answer



          share|improve this answer











          answered Jun 21 at 21:27









          Roland Illig

          10.4k11543




          10.4k11543











          • ⟪Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.⟫ Using both is OK, since the slower #ifdef is a fallback if the fast #pragma is not supported. But, since this is not portable anyway, might as well count on the #pragma.
            – JDługosz
            Jun 22 at 8:37










          • ⟪Since methods [sic] must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense.⟫ It has become common place, not common sense. Others will say that all such tags are to be avoided because the algorithm should not care where the value comes from, and in fact they tend to migrate as code is made more general.
            – JDługosz
            Jun 22 at 8:40
















          • ⟪Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.⟫ Using both is OK, since the slower #ifdef is a fallback if the fast #pragma is not supported. But, since this is not portable anyway, might as well count on the #pragma.
            – JDługosz
            Jun 22 at 8:37










          • ⟪Since methods [sic] must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense.⟫ It has become common place, not common sense. Others will say that all such tags are to be avoided because the algorithm should not care where the value comes from, and in fact they tend to migrate as code is made more general.
            – JDługosz
            Jun 22 at 8:40















          ⟪Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.⟫ Using both is OK, since the slower #ifdef is a fallback if the fast #pragma is not supported. But, since this is not portable anyway, might as well count on the #pragma.
          – JDługosz
          Jun 22 at 8:37




          ⟪Choose between the #ifndef inclusion guard and pragma once. Using both is redundant.⟫ Using both is OK, since the slower #ifdef is a fallback if the fast #pragma is not supported. But, since this is not portable anyway, might as well count on the #pragma.
          – JDługosz
          Jun 22 at 8:37












          ⟪Since methods [sic] must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense.⟫ It has become common place, not common sense. Others will say that all such tags are to be avoided because the algorithm should not care where the value comes from, and in fact they tend to migrate as code is made more general.
          – JDługosz
          Jun 22 at 8:40




          ⟪Since methods [sic] must not have the same name as fields, prefixing all fields with m_ or some similar prefix has become common sense.⟫ It has become common place, not common sense. Others will say that all such tags are to be avoided because the algorithm should not care where the value comes from, and in fact they tend to migrate as code is made more general.
          – JDługosz
          Jun 22 at 8:40












          up vote
          4
          down vote














          Just some basic stuff, will add more later.




          Strings



          The implementation uses a lot of hard-coded strings and is inconsistent regarding their encodings. Some strings are "normal" ASCII ones whereas others are wide strings. This might cause a lot of unforeseen problems.



          Randomness



          utility::get_random might actually always return the same result for the same inputs. Depending on the standard library implementation, std::random_device might return the same sequence for each created object.



          Since utility::get_random always creates a new object this basically "resets" the sequence for those implementations. (It's conceptually equivalent to srand(0); /* do something with */ rand();).



          This can be fixed by marking rd as static so it only gets instantiated once. (And while we're at it, it might make sense to make mt static as well, since its construction is quite costly).



          Variable naming



          Many variables have very short and very cryptic names. This makes reasoning about code a lot harder than it has to be. Trying to understand the code gets interrupted by questions like "What is the difference between s and ss?", where a better choice of names would make their usage obvious.



          Magic numbers/strings



          There are very many hard-coded strings and numbers, and not always is it obvious what they are meant for.



          Example:



          settings 10, L"spanish", L"german", 0, 15 // taken from the menu constructor


          It isn't obvious at all what all those values represent. Compare this with:



          settings threshold, source_lang, target_lang, colors::black, colors::white 


          With this, I as a reader of the code don't have to guess what all those values mean.



          Other stuff



          • Nearly all the work is done in the menu constructor. This makes it very hard to reuse or even test that class.





          share|improve this answer























          • i guess it would be better to split the menues in smaller classes? do you have a suggestion?
            – Sandro4912
            Jun 23 at 9:48














          up vote
          4
          down vote














          Just some basic stuff, will add more later.




          Strings



          The implementation uses a lot of hard-coded strings and is inconsistent regarding their encodings. Some strings are "normal" ASCII ones whereas others are wide strings. This might cause a lot of unforeseen problems.



          Randomness



          utility::get_random might actually always return the same result for the same inputs. Depending on the standard library implementation, std::random_device might return the same sequence for each created object.



          Since utility::get_random always creates a new object this basically "resets" the sequence for those implementations. (It's conceptually equivalent to srand(0); /* do something with */ rand();).



          This can be fixed by marking rd as static so it only gets instantiated once. (And while we're at it, it might make sense to make mt static as well, since its construction is quite costly).



          Variable naming



          Many variables have very short and very cryptic names. This makes reasoning about code a lot harder than it has to be. Trying to understand the code gets interrupted by questions like "What is the difference between s and ss?", where a better choice of names would make their usage obvious.



          Magic numbers/strings



          There are very many hard-coded strings and numbers, and not always is it obvious what they are meant for.



          Example:



          settings 10, L"spanish", L"german", 0, 15 // taken from the menu constructor


          It isn't obvious at all what all those values represent. Compare this with:



          settings threshold, source_lang, target_lang, colors::black, colors::white 


          With this, I as a reader of the code don't have to guess what all those values mean.



          Other stuff



          • Nearly all the work is done in the menu constructor. This makes it very hard to reuse or even test that class.





          share|improve this answer























          • i guess it would be better to split the menues in smaller classes? do you have a suggestion?
            – Sandro4912
            Jun 23 at 9:48












          up vote
          4
          down vote










          up vote
          4
          down vote










          Just some basic stuff, will add more later.




          Strings



          The implementation uses a lot of hard-coded strings and is inconsistent regarding their encodings. Some strings are "normal" ASCII ones whereas others are wide strings. This might cause a lot of unforeseen problems.



          Randomness



          utility::get_random might actually always return the same result for the same inputs. Depending on the standard library implementation, std::random_device might return the same sequence for each created object.



          Since utility::get_random always creates a new object this basically "resets" the sequence for those implementations. (It's conceptually equivalent to srand(0); /* do something with */ rand();).



          This can be fixed by marking rd as static so it only gets instantiated once. (And while we're at it, it might make sense to make mt static as well, since its construction is quite costly).



          Variable naming



          Many variables have very short and very cryptic names. This makes reasoning about code a lot harder than it has to be. Trying to understand the code gets interrupted by questions like "What is the difference between s and ss?", where a better choice of names would make their usage obvious.



          Magic numbers/strings



          There are very many hard-coded strings and numbers, and not always is it obvious what they are meant for.



          Example:



          settings 10, L"spanish", L"german", 0, 15 // taken from the menu constructor


          It isn't obvious at all what all those values represent. Compare this with:



          settings threshold, source_lang, target_lang, colors::black, colors::white 


          With this, I as a reader of the code don't have to guess what all those values mean.



          Other stuff



          • Nearly all the work is done in the menu constructor. This makes it very hard to reuse or even test that class.





          share|improve this answer
















          Just some basic stuff, will add more later.




          Strings



          The implementation uses a lot of hard-coded strings and is inconsistent regarding their encodings. Some strings are "normal" ASCII ones whereas others are wide strings. This might cause a lot of unforeseen problems.



          Randomness



          utility::get_random might actually always return the same result for the same inputs. Depending on the standard library implementation, std::random_device might return the same sequence for each created object.



          Since utility::get_random always creates a new object this basically "resets" the sequence for those implementations. (It's conceptually equivalent to srand(0); /* do something with */ rand();).



          This can be fixed by marking rd as static so it only gets instantiated once. (And while we're at it, it might make sense to make mt static as well, since its construction is quite costly).



          Variable naming



          Many variables have very short and very cryptic names. This makes reasoning about code a lot harder than it has to be. Trying to understand the code gets interrupted by questions like "What is the difference between s and ss?", where a better choice of names would make their usage obvious.



          Magic numbers/strings



          There are very many hard-coded strings and numbers, and not always is it obvious what they are meant for.



          Example:



          settings 10, L"spanish", L"german", 0, 15 // taken from the menu constructor


          It isn't obvious at all what all those values represent. Compare this with:



          settings threshold, source_lang, target_lang, colors::black, colors::white 


          With this, I as a reader of the code don't have to guess what all those values mean.



          Other stuff



          • Nearly all the work is done in the menu constructor. This makes it very hard to reuse or even test that class.






          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Jun 21 at 21:48


























          answered Jun 21 at 21:29









          hoffmale

          4,235630




          4,235630











          • i guess it would be better to split the menues in smaller classes? do you have a suggestion?
            – Sandro4912
            Jun 23 at 9:48
















          • i guess it would be better to split the menues in smaller classes? do you have a suggestion?
            – Sandro4912
            Jun 23 at 9:48















          i guess it would be better to split the menues in smaller classes? do you have a suggestion?
          – Sandro4912
          Jun 23 at 9:48




          i guess it would be better to split the menues in smaller classes? do you have a suggestion?
          – Sandro4912
          Jun 23 at 9:48










          up vote
          2
          down vote













          int find(std::wstring s);


          Why is this passed by value (making a copy)? And I find it odd that “checks if words are in file and returns row of it” would also modify the file object!



          So, should this really be



          rownum_t find (const std::wstring& s) const;


          ? Note also I changed the return type to indicate its actual domain. Even if you just make that a typedef with no compile-time checking, it still is good human documentation.



          std::vector<Vocabulary> get_part(const int begin, const int end);


          Good that you’re returning the vector by value as a normal return value — many people try to avoid that for outdated reasons. But, again you did not make the function itself const, so you are implying that calling this will modify the file object.



          Second, the const in the parameters don’t have meaning there. It is useful in a function definition, but ignored in a declaration so omitted as not being part of the interface contract.



          As far as the use of wide strings (note for others: On Windows wchar_t is 16 bits), it is necessary for calling Win32 API functions. But for most uses in the program, it is easier to just use utf8 everywhere.



          Note that C++ supports (portable) UTF8 string literals now, so you can write



          const char word = u8"Füße";


          will show up in the source editor and code UTF-8 bytes in the character array, regardless of what ANSI code page is in effect when you run the compiler.



          For display: if you are outputting to the Windows Console, the normal 8-bit stream will use the current code page. In the past, I’ve had inconsistent results using Microsoft’s library just switching to the wide calls — but that was years ago; maybe it’s fixed now. I got proper output by using the raw Win32 console output commands instead of the stream wrappers that feed standard output to the console.



          Meanwhile, the font loaded in the Console will not display characters outside of the current code page (or closely related ones). You may need to switch to a Unicode font in the Console’s system menu. A simple program to print the alphabet (in each language) will tell you if it is OK to proceed.






          share|improve this answer





















          • does that mean i can live without using wstring in the whole programm?
            – Sandro4912
            Jun 23 at 9:44










          • Use wstring inside your wrapper functions that call the Win32 API xxxxW functions.
            – JDługosz
            Jun 23 at 9:48










          • what do you mean with Win32 API xxxxW functions? Im sorry i cant follow. You mean if i use console and file io in the program?
            – Sandro4912
            Jun 25 at 15:35










          • Windows API functions come in pairs. E.g. you have WriteConsoleA and WriteConsoleW, CreateFileA and CreateFileW. I was referring to the –W forms, which take wchar_t encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to use wchar_t inside the wrappers.
            – JDługosz
            Jun 26 at 18:12














          up vote
          2
          down vote













          int find(std::wstring s);


          Why is this passed by value (making a copy)? And I find it odd that “checks if words are in file and returns row of it” would also modify the file object!



          So, should this really be



          rownum_t find (const std::wstring& s) const;


          ? Note also I changed the return type to indicate its actual domain. Even if you just make that a typedef with no compile-time checking, it still is good human documentation.



          std::vector<Vocabulary> get_part(const int begin, const int end);


          Good that you’re returning the vector by value as a normal return value — many people try to avoid that for outdated reasons. But, again you did not make the function itself const, so you are implying that calling this will modify the file object.



          Second, the const in the parameters don’t have meaning there. It is useful in a function definition, but ignored in a declaration so omitted as not being part of the interface contract.



          As far as the use of wide strings (note for others: On Windows wchar_t is 16 bits), it is necessary for calling Win32 API functions. But for most uses in the program, it is easier to just use utf8 everywhere.



          Note that C++ supports (portable) UTF8 string literals now, so you can write



          const char word = u8"Füße";


          will show up in the source editor and code UTF-8 bytes in the character array, regardless of what ANSI code page is in effect when you run the compiler.



          For display: if you are outputting to the Windows Console, the normal 8-bit stream will use the current code page. In the past, I’ve had inconsistent results using Microsoft’s library just switching to the wide calls — but that was years ago; maybe it’s fixed now. I got proper output by using the raw Win32 console output commands instead of the stream wrappers that feed standard output to the console.



          Meanwhile, the font loaded in the Console will not display characters outside of the current code page (or closely related ones). You may need to switch to a Unicode font in the Console’s system menu. A simple program to print the alphabet (in each language) will tell you if it is OK to proceed.






          share|improve this answer





















          • does that mean i can live without using wstring in the whole programm?
            – Sandro4912
            Jun 23 at 9:44










          • Use wstring inside your wrapper functions that call the Win32 API xxxxW functions.
            – JDługosz
            Jun 23 at 9:48










          • what do you mean with Win32 API xxxxW functions? Im sorry i cant follow. You mean if i use console and file io in the program?
            – Sandro4912
            Jun 25 at 15:35










          • Windows API functions come in pairs. E.g. you have WriteConsoleA and WriteConsoleW, CreateFileA and CreateFileW. I was referring to the –W forms, which take wchar_t encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to use wchar_t inside the wrappers.
            – JDługosz
            Jun 26 at 18:12












          up vote
          2
          down vote










          up vote
          2
          down vote









          int find(std::wstring s);


          Why is this passed by value (making a copy)? And I find it odd that “checks if words are in file and returns row of it” would also modify the file object!



          So, should this really be



          rownum_t find (const std::wstring& s) const;


          ? Note also I changed the return type to indicate its actual domain. Even if you just make that a typedef with no compile-time checking, it still is good human documentation.



          std::vector<Vocabulary> get_part(const int begin, const int end);


          Good that you’re returning the vector by value as a normal return value — many people try to avoid that for outdated reasons. But, again you did not make the function itself const, so you are implying that calling this will modify the file object.



          Second, the const in the parameters don’t have meaning there. It is useful in a function definition, but ignored in a declaration so omitted as not being part of the interface contract.



          As far as the use of wide strings (note for others: On Windows wchar_t is 16 bits), it is necessary for calling Win32 API functions. But for most uses in the program, it is easier to just use utf8 everywhere.



          Note that C++ supports (portable) UTF8 string literals now, so you can write



          const char word = u8"Füße";


          will show up in the source editor and code UTF-8 bytes in the character array, regardless of what ANSI code page is in effect when you run the compiler.



          For display: if you are outputting to the Windows Console, the normal 8-bit stream will use the current code page. In the past, I’ve had inconsistent results using Microsoft’s library just switching to the wide calls — but that was years ago; maybe it’s fixed now. I got proper output by using the raw Win32 console output commands instead of the stream wrappers that feed standard output to the console.



          Meanwhile, the font loaded in the Console will not display characters outside of the current code page (or closely related ones). You may need to switch to a Unicode font in the Console’s system menu. A simple program to print the alphabet (in each language) will tell you if it is OK to proceed.






          share|improve this answer













          int find(std::wstring s);


          Why is this passed by value (making a copy)? And I find it odd that “checks if words are in file and returns row of it” would also modify the file object!



          So, should this really be



          rownum_t find (const std::wstring& s) const;


          ? Note also I changed the return type to indicate its actual domain. Even if you just make that a typedef with no compile-time checking, it still is good human documentation.



          std::vector<Vocabulary> get_part(const int begin, const int end);


          Good that you’re returning the vector by value as a normal return value — many people try to avoid that for outdated reasons. But, again you did not make the function itself const, so you are implying that calling this will modify the file object.



          Second, the const in the parameters don’t have meaning there. It is useful in a function definition, but ignored in a declaration so omitted as not being part of the interface contract.



          As far as the use of wide strings (note for others: On Windows wchar_t is 16 bits), it is necessary for calling Win32 API functions. But for most uses in the program, it is easier to just use utf8 everywhere.



          Note that C++ supports (portable) UTF8 string literals now, so you can write



          const char word = u8"Füße";


          will show up in the source editor and code UTF-8 bytes in the character array, regardless of what ANSI code page is in effect when you run the compiler.



          For display: if you are outputting to the Windows Console, the normal 8-bit stream will use the current code page. In the past, I’ve had inconsistent results using Microsoft’s library just switching to the wide calls — but that was years ago; maybe it’s fixed now. I got proper output by using the raw Win32 console output commands instead of the stream wrappers that feed standard output to the console.



          Meanwhile, the font loaded in the Console will not display characters outside of the current code page (or closely related ones). You may need to switch to a Unicode font in the Console’s system menu. A simple program to print the alphabet (in each language) will tell you if it is OK to proceed.







          share|improve this answer













          share|improve this answer



          share|improve this answer











          answered Jun 22 at 9:04









          JDługosz

          5,047731




          5,047731











          • does that mean i can live without using wstring in the whole programm?
            – Sandro4912
            Jun 23 at 9:44










          • Use wstring inside your wrapper functions that call the Win32 API xxxxW functions.
            – JDługosz
            Jun 23 at 9:48










          • what do you mean with Win32 API xxxxW functions? Im sorry i cant follow. You mean if i use console and file io in the program?
            – Sandro4912
            Jun 25 at 15:35










          • Windows API functions come in pairs. E.g. you have WriteConsoleA and WriteConsoleW, CreateFileA and CreateFileW. I was referring to the –W forms, which take wchar_t encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to use wchar_t inside the wrappers.
            – JDługosz
            Jun 26 at 18:12
















          • does that mean i can live without using wstring in the whole programm?
            – Sandro4912
            Jun 23 at 9:44










          • Use wstring inside your wrapper functions that call the Win32 API xxxxW functions.
            – JDługosz
            Jun 23 at 9:48










          • what do you mean with Win32 API xxxxW functions? Im sorry i cant follow. You mean if i use console and file io in the program?
            – Sandro4912
            Jun 25 at 15:35










          • Windows API functions come in pairs. E.g. you have WriteConsoleA and WriteConsoleW, CreateFileA and CreateFileW. I was referring to the –W forms, which take wchar_t encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to use wchar_t inside the wrappers.
            – JDługosz
            Jun 26 at 18:12















          does that mean i can live without using wstring in the whole programm?
          – Sandro4912
          Jun 23 at 9:44




          does that mean i can live without using wstring in the whole programm?
          – Sandro4912
          Jun 23 at 9:44












          Use wstring inside your wrapper functions that call the Win32 API xxxxW functions.
          – JDługosz
          Jun 23 at 9:48




          Use wstring inside your wrapper functions that call the Win32 API xxxxW functions.
          – JDługosz
          Jun 23 at 9:48












          what do you mean with Win32 API xxxxW functions? Im sorry i cant follow. You mean if i use console and file io in the program?
          – Sandro4912
          Jun 25 at 15:35




          what do you mean with Win32 API xxxxW functions? Im sorry i cant follow. You mean if i use console and file io in the program?
          – Sandro4912
          Jun 25 at 15:35












          Windows API functions come in pairs. E.g. you have WriteConsoleA and WriteConsoleW, CreateFileA and CreateFileW. I was referring to the –W forms, which take wchar_t encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to use wchar_t inside the wrappers.
          – JDługosz
          Jun 26 at 18:12




          Windows API functions come in pairs. E.g. you have WriteConsoleA and WriteConsoleW, CreateFileA and CreateFileW. I was referring to the –W forms, which take wchar_t encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to use wchar_t inside the wrappers.
          – JDługosz
          Jun 26 at 18:12












           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f197007%2fconsole-based-vocabulary-trainer%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Greedy Best First Search implementation in Rust

          Function to Return a JSON Like Objects Using VBA Collections and Arrays

          C++11 CLH Lock Implementation