Console based Vocabulary Trainer
Clash 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"~");
c++ file console windows quiz
add a comment |Â
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"~");
c++ file console windows quiz
add a comment |Â
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"~");
c++ file console windows quiz
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"~");
c++ file console windows quiz
edited Jun 21 at 21:18
200_success
123k14143399
123k14143399
asked Jun 21 at 19:59
Sandro4912
467119
467119
add a comment |Â
add a comment |Â
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.
âª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
add a comment |Â
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.
i guess it would be better to split the menues in smaller classes? do you have a suggestion?
â Sandro4912
Jun 23 at 9:48
add a comment |Â
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.
does that mean i can live without using wstring in the whole programm?
â Sandro4912
Jun 23 at 9:44
Usewstring
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 haveWriteConsoleA
andWriteConsoleW
,CreateFileA
andCreateFileW
. I was referring to theâÂÂW
forms, which takewchar_t
encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to usewchar_t
inside the wrappers.
â JDà Âugosz
Jun 26 at 18:12
add a comment |Â
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.
âª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
add a comment |Â
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.
âª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
add a comment |Â
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.
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.
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
add a comment |Â
âª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
add a comment |Â
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.
i guess it would be better to split the menues in smaller classes? do you have a suggestion?
â Sandro4912
Jun 23 at 9:48
add a comment |Â
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.
i guess it would be better to split the menues in smaller classes? do you have a suggestion?
â Sandro4912
Jun 23 at 9:48
add a comment |Â
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.
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.
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
add a comment |Â
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
add a comment |Â
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.
does that mean i can live without using wstring in the whole programm?
â Sandro4912
Jun 23 at 9:44
Usewstring
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 haveWriteConsoleA
andWriteConsoleW
,CreateFileA
andCreateFileW
. I was referring to theâÂÂW
forms, which takewchar_t
encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to usewchar_t
inside the wrappers.
â JDà Âugosz
Jun 26 at 18:12
add a comment |Â
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.
does that mean i can live without using wstring in the whole programm?
â Sandro4912
Jun 23 at 9:44
Usewstring
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 haveWriteConsoleA
andWriteConsoleW
,CreateFileA
andCreateFileW
. I was referring to theâÂÂW
forms, which takewchar_t
encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to usewchar_t
inside the wrappers.
â JDà Âugosz
Jun 26 at 18:12
add a comment |Â
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.
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.
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
Usewstring
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 haveWriteConsoleA
andWriteConsoleW
,CreateFileA
andCreateFileW
. I was referring to theâÂÂW
forms, which takewchar_t
encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to usewchar_t
inside the wrappers.
â JDà Âugosz
Jun 26 at 18:12
add a comment |Â
does that mean i can live without using wstring in the whole programm?
â Sandro4912
Jun 23 at 9:44
Usewstring
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 haveWriteConsoleA
andWriteConsoleW
,CreateFileA
andCreateFileW
. I was referring to theâÂÂW
forms, which takewchar_t
encoded as UTF-16 or sometimes only UCS2. Wrap those functions so you only need to usewchar_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
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password