没有 "goto" 的正确重试的 C++ 用户输入限制

C++ user input restriction with proper retry without "goto"

我有以下代码:

qstn:
  cout << "Input customer's lastname: ";
  getline(cin, lname);

  if (lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ") != string::npos) {
      cout << "You can only input alpha here!\n";
      cin.clear();
      goto qstn;
  } else if (lname.empty()) {
      cout << "Please enter your firstname!\n";
      cin.clear();
      goto qstn;
  }

  int lnamel = lname.length();
  int strl = str.length();
  int is = 0;

  for (int i = 1; i < strl;) {
      i++;
      is++;

      if (lname[i] == lname[is] && lname[i] == ' ' || lname[0] == ' ') {
          cin.clear();
          cout << "Please input your lastname properly!\n";
          goto qstn;
      }
  }
  // next question here

我很难思考什么是正确的逻辑来避免 goto 声明,自从我上大学以来我就在使用它,但这里有人说 使用它根本不好,因为它可能会破坏我的代码。

我尝试使用 do while 循环,但它不像 goto 那样流畅。

请帮忙!

While 循环看起来是您的最佳选择。您可以使用 continue 关键字重做循环。

int incorrect = 0;
while(!incorrect) {
cout<<"Input customer's lastname: ";
        getline(cin,lname);

        if(lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")!=string::npos)
            {
            cout<<"You can only input alpha here!\n";
            cin.clear();
            continue;
            }
        else if(lname.empty())
        {
            cout<<"Please enter your firstname!\n";
            cin.clear();
            continue;
        }
        int lnamel=lname.length();
        int strl=str.length();
        int is=0;
        for(int i=1; i<strl;)
        {
           i++;
           is++;
           if(lname[i]==lname[is]&&lname[i]==' '||lname[0]==' ')
           {
               cin.clear();
               cout<<"Please input your lastname properly!\n";
               continue;
           }
           incorrect = 1;
        }

您需要使用一些 do while 循环 例如这个:

qstn:
            cout<<"Input customer's lastname: ";
            getline(cin,lname);

            if(lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")!=string::npos)
                {
                cout<<"You can only input alpha here!\n";
                cin.clear();
                goto qstn;
                }
            else if(lname.empty())
            {
                cout<<"Please enter your firstname!\n";
                cin.clear();
                goto qstn;
            }

可以重写为:

int flag;
do{
    flag = 1;
    cout<<"Input customer's lastname: ";
    getline(cin,lname);
    if(lname.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")!=string::npos)
    {
        flag = 0;
        cout<<"You can only input alpha here!\n";
    }
    else if(lname.empty())
    {
        flag = 0;
        cout<<"Please enter your firstname!\n";
    }
    cin.clear();
} while( flag !=1 );

随意使用布尔类型标志,这并不重要

使用函数:

bool getLastName(string & lname,
                 string & str)
{
    cout << "Input customer's lastname: ";
    getline(cin, lname);

    if (lname.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ")
            != string::npos)
    {
        cout << "You can only input alpha here!\n";
        cin.clear();
        return false;
    }
    else if (lname.empty())
    {
        cout << "Please enter your firstname!\n";
        cin.clear();
        return false;
    }
    int lnamel = lname.length();
    int strl = str.length();
    int is = 0;
    for (int i = 1; i < strl;)
    {
        i++;
        is++;
        if (lname[i] == lname[is] && lname[i] == ' ' || lname[0] == ' ')
        {
            cin.clear();
            cout << "Please input your lastname properly!\n";
            return false;
        }
    }
    return true;
}

我在这里所做的就是将 goto 替换为 return false。如果程序到达函数的末尾,return true。在 while 循环中调用函数:

while (!getLastName(lname, str))
{
    // do nothing
}

这不仅使代码去面条化,而且将其分解成漂亮、小、易于管理的部分。这叫做procedural programming

这是我喜欢用的成语:

int i;

if (std::cin >> prompt("enter an integer: ", i))
{
    std::cout << "Read user input: " << i << "\n";
} else {
    std::cout << "Input failed (too many attempts). Eof? " << std::boolalpha << std::cin.eof() << "\n";
}

这里,prompt 是一个智能输入操纵器,负责处理解析错误或流失败并重试。

它非常通用,所以实际上可以做很多事情,但您不需要指明所有选项。当操纵器插入到流中时,它会中继到 do_manip 成员:

template <typename Char, typename CharT>
friend std::basic_istream<Char, CharT>& operator>>(std::basic_istream<Char, CharT>& is, checked_input<T, Prompter>& manip) {
    return manip.do_manip(is);
}

do_manip 处理所有逻辑 而没有任何 gotos :) :

std::istream& do_manip(std::istream& is) {
    auto attempt = [this] { return infinite() || retries_ > 0; };

    while (attempt()) {
        if (!infinite())
            retries_ -= 1;

        prompter_(out_);

        if (is >> value_) {
            if (!run_validators(out_))
                is.setstate(is.rdstate() | std::ios::failbit);
            else
                break;
        } else {
            out_.get() << format_error_ << "\n";
        }

        if (attempt()) {
            is.clear();
            if (flush_on_error_)
                is.ignore(1024, '\n');
        }
    }

    return is;
}

您可以看到在接受输入之前可以进行验证 运行。

这是一个比较成熟的演示:

Live On Coliru

int main() {
    using namespace inputmagic;

    int i;

    if (std::cin >> prompt("enter an integer: ", i)
            .retries(3)
            .flush_on_error(false)
            .format_error("I couldn't read that (Numbers look like 123)")
            .output(std::cerr)
            .validate([](int v) { return v > 3 && v < 88; }, "value not in range (3,88)")
            .validate([](int v) { return 0 == v % 2; })
            .validate([](int v) { return v != 42; }, "The Answer Is Forbidden")
            .multiple_diagnostics())
    {
        std::cout << "Read user input: " << i << "\n";
    } else {
        std::cout << "Input failed (too many attempts). Eof? " << std::boolalpha << std::cin.eof() << "\n";
    }
}

你可以看到它只接受有效的整数

  • >3 且 <88,
  • 偶数
  • 除了42(禁号)

在随后的重试中输入数字 21、42 和 10 时,您将得到:live

enter an integer: 21
Value not valid
enter an integer: 42
The Answer Is Forbidden
enter an integer: 10
Read user input: 10

但是,如果您一直输入 1,您会得到:live

enter an integer: 1
value not in range (3,88)
Value not valid
enter an integer: 1
value not in range (3,88)
Value not valid
enter an integer: 1
value not in range (3,88)
Value not valid
Input failed (too many attempts). Eof? false

或者如果您从单行文件读取:live

enter an integer: value not in range (3,88)
Value not valid
enter an integer: I couldn't read that (Numbers look like 123)
enter an integer: I couldn't read that (Numbers look like 123)
Input failed (too many attempts). Eof? true

在我看来,您的代码的目的不明确。

您显然不希望输入的字符串包含前导 space,也不希望包含多个连续的 space。除此之外,只接受字母字符。

如果用户确实输入了多个连续的 space,我可能会忽略除第一个以外的所有内容。我可能会这样写代码:

#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>
#include <sstream>

bool non_alpha(std::string const &s) {
    return !std::all_of(s.begin(), s.end(), [](unsigned char c) { return std::isalpha(c) || std::isspace(c); });
}

std::string get_name(std::string const &prompt) {
    std::string result;
    std::string line;

    do {
        std::cout << prompt;
        std::getline(std::cin, line);
    } while (non_alpha(line));

    std::istringstream words(line);
    std::string word;
    while (words >> word)
        result += word + ' ';
    return result;
}

int main() {
    auto res = get_name("Please enter last name, alpha-only\n");
    if (res.empty())
        std::cout << "Oh well, maybe some other time";
    else
        std::cout << "Thanks Mr(s). " << res << "\n";
}

我很想考虑对非字母字符做大致相同的事情——而不是要求用户从头重新输入,假设这是一个错误并忽略它。