1

I have a question that has evolved from one of my previous (Conditionally Breaking A Long Sequence Of Inputs?), but so far, no one has been able to give me a satisfactory answer, and all my efforts have failed.

I am trying to allow the user to break early from entering input if they don't need to. i.e., for the code below:

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.

Up to 12 monsters are actually supported. If the user wants to use only, say, 3-4 of them, I'd like them to be able to skip the rest and save themselves a lot of keystrokes. I've already made sure to initialize all the variables to 0, and have a function to remove 0 elements from the storage vector later. All that's missing is getting away from this input chain. I have tried various forms of a while loop wrapping, like this:

while(cin.get() != '#') {
   cout << '\n' << "Monster A's name is: ";
   cin >> nameA;
   //etc...
}

But upon entering the desired character, the code simply outputs all the prompts ("Monster A's name is:" , etc.) over and over again, without moving on or accepting further input. It's like the code is stuck in an infinite loop, even though it should be leaving the loop on # input.

Any ideas? Been really stuck on this for a while and would be very grateful if anyone could offer an alternate solution, or at least let me know the flaw in my own.

Thanks!

4

5 回答 5

2

您的代码会引发无限循环,因为 std::cin 的运算符 >> 不采用“行尾”字符(回车键),因此它仍在流中。

因此,当您第二次到达 cin.get() 时,缓冲区中仍然有一个字符(从您验证怪物名称时开始输入)。cin.get() 接受它,看到它不是'#',然后转到下一个 std::cin,它做同样的事情。您可以通过忽略 char 来修复此行为:

while(cin.get() != '#')
{
   cout << '\n' << "Monster A's name is: ";
   cin >> nameA;
   //etc...
   cin.ignore();
}
于 2013-06-17T15:15:21.090 回答
0

面向对象和 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;
}
于 2013-06-17T15:44:14.227 回答
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();
}

如您所见,将每个单独的关注点分解到一个单独的函数中会产生更简单的代码。

于 2013-06-17T17:10:23.273 回答
0

我刚刚测试了这组代码,它似乎可以按照您的意愿工作。当然,您必须对其进行修改以适合您的原始应用程序。

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++;
}

希望这可以帮助。

于 2013-06-17T14:56:16.807 回答
-1

你能找出信息,例如“按# 跳过这个问题”吗?而不是让用户猜测哪个键跳过问题?

于 2013-06-17T14:44:22.573 回答