У меня есть вопрос, который возник из одного из моих предыдущих (Условно прерывая длинную последовательность входов?), но до сих пор никто не смог дать мне удовлетворительного ответа, и все мои усилия провалились.
Я пытаюсь позволить пользователю прервать ввод данных, если им это не нужно. то есть для кода ниже:
cout << '\n' << "Monster A's name is: ";
cin >> nameA;
cout << '\n' << "Monster A rolled: ";
cin >> rollM_A;
cout << '\n' << "Monster A's Dex is: ";
cin >> DexA;
cout << '\n' << "Monster A's Mod is: ";
cin >> ModA;
cout << '\n' << "Monster A's Level is: ";
cin >> LvlA;
//etc.
На самом деле поддерживается до 12 монстров. Если пользователь хочет использовать только, скажем, 3-4 из них, я бы хотел, чтобы они могли пропустить остальные и сэкономить много нажатий клавиш. Я уже удостоверился, что инициализировал все переменные к 0, и имел функцию для удаления 0 элементов из вектора хранения позже. Все, что не хватает, это убежать от этой цепочки ввода. Я пробовал различные формы упаковки цикла while, например:
while(cin.get() != '#') {
cout << '\n' << "Monster A's name is: ";
cin >> nameA;
//etc...
}
Но после ввода нужного символа код просто выводит все подсказки («Имя монстра А:» и т. Д.) Снова и снова, не переходя и не принимая дальнейший ввод. Это похоже на то, что код застрял в бесконечном цикле, даже если он должен выходить из цикла на входе #.
Есть идеи? Я действительно застрял на этом некоторое время и был бы очень признателен, если бы кто-нибудь мог предложить альтернативное решение или, по крайней мере, дать мне знать о недостатке в моем собственном.
Спасибо!
Ваш кусок кода провоцирует бесконечный цикл, потому что оператор std :: cin >> не принимает символ «конец строки» (клавиша ввода), поэтому он все еще находится в потоке.
Поэтому, когда вы заходите в cin.get () во второй раз, в буфере все еще остается символ (введите, когда вы проверили имя монстра). cin.get () принимает его, видит, что это не ‘#’, и переходит к следующему std :: cin, который делает то же самое. Вы можете исправить это поведение, игнорируя char:
while(cin.get() != '#')
{
cout << '\n' << "Monster A's name is: ";
cin >> nameA;
//etc...
cin.ignore();
}
Я только что проверил этот набор кода, и он, кажется, работает так, как вы хотите. Конечно, вам придется изменить его в соответствии с вашим исходным приложением.
std::string in;
while (true) {
std::cout << "Enter a name\n";
std::cin >> in;
if (in == "#")
break;
std::cout << "\nMonster A's name is: " << in << "\n";
}
Чтобы включить ограничение количества монстров, а не иметь true
параметр, передаваемый в цикл while, просто добавляет счетчик к числу созданных монстров и нарушает это условие:
int num_monsters = 0;
while (num_monsters <= 12) {
...
num_monsters++;
}
Надеюсь это поможет.
Объектная ориентация и stdlib на помощь:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Monster {
string name;
int rolled, dex, mod, level;
template<class input, class output, class container>
friend input& read_monster(input& i, output& o, container &c) {
string n;
o << "\nMonster's name is: " << flush;
i >> n;
if( !i || (n == "#") ) {
o << "\nEnd of monster list";
i.setstate(ios::failbit);
return i;
}
Monster m;
m.name = n;
o << "\nMonster's rolled is " << flush;
i >> m.rolled;
if( !i ) return i;
o << "\nMonster's dex is " << flush;
i >> m.dex;
if( !i ) return i;
o << "\nMonster's mod is " << flush;
i >> m.mod;
if( !i ) return i;
o << "\nMonster's level is " << flush;
i >> m.level;
if( !i ) return i;
o << "\n";
c.push_back(m);
return i;
}
template<class output>
friend output& operator<<(output& o, Monster&m) {
return o << "Monster('" << m.name <<"', R/" << m.rolled
<< ", D/" << m.dex << ", M/" << m.mod << ", L/" << m.level
<< ")\n";
}
Monster() : name(), rolled(), dex(), mod(), level() {}
};
int main(int, char**) {
vector<Monster> v;
while( read_monster(cin, cout, v) && (v.size() <= 12) )
;
for( auto m: v )
cout << m;
return 0;
}
Так как вы делаете линейно-ориентированный ввод, вы действительно должны быть
с помощью std::getline
читать. И так как линейно-ориентированный ввод
состоит из блоков линий, каждый из которых определяет объект, вы должны
создайте класс и прочитайте его. (И кроме того, вам понадобится
гораздо больше обработки ошибок, так как вам нужно обрабатывать ошибки в
любая из промежуточных строк.)
Очевидно, что каждая из этих проблем должна быть разделена на
отдельная функция:
std::string
getLineFromPrompt( std::string const& prompt )
{
std::string results;
std::cout << prompt;
std::getline( std::cin, results );
return results;
}
template <typename T>
bool
tryGetValueFromPrompt( std::string const& prompt, T& value )
{
std::string line = getLineFromPrompt( prompt );
std::istringstream parser( line );
return parser >> value >> std::ws && parser.get() == EOF;
}
template <typename T>
void
getValueFromPrompt( std::string const& prompt, T& value )
{
while ( ! tryGetValueFromPrompt( prompt, value ) ) {
std::cout << "Illegal input, try again" << std::endl;
}
}
class Monster
{
std::string name;
int roll;
int dexterity;
int mod;
int level;
public:
Monster( std::string const& name )
: name( name )
{
getValueFromPromt( name + " rolled:", roll );
getValueFromPromt( name + " dexterity:", dexterity );
getValueFromPromt( name + " mod:", mod );
getValueFromPromt( name + " level:", level );
}
};
bool
isEndFlag( std::string const& line )
{
return line.empty() || line[0] == '#';
}
И наконец, цикл:
std::vector <Monster> monsters;
std::string nextName = getLineFromPrompt();
while ( ! isEndFlag( nextName ) ) {
monsters.push_back( Monster( nextName ) );
nextName = getLineFromPrompt();
}
Как вы можете видеть, вычленяя каждую отдельную проблему в
отдельная функция приводит к гораздо более простому коду.
Можете ли вы следить за информацией, например, ‘нажмите #, чтобы пропустить этот вопрос’? Вместо того, чтобы позволить пользователю угадать, какой ключ пропустить вопрос?