我打算接受以下所有答案:例如yeS,yES,我本来不打算接受,但这是我的代码的结果。
No和它的变体版本也是如此

它完美无缺,或者看起来如此。有更好的方法吗?

评论

在这种情况下,我只是将其推迟到正则表达式库中。易于改进和扩展。另外,如果您的审阅者/经理知道正则表达式,它会更安全。他可以验证它,因为它有些标准化。

#1 楼

您有一个常见的错误,它将使您陷入无限循环。 :-)

读取数据(尤其是用户输入数据)时,必须验证读取是否正常工作。如果读取失败并在流上设置了错误标志之一,则随后的任何读取尝试均将被忽略。

因此,通常在循环测试中检查读取操作。

std::string word;
while(word != "STOP") {
    std::cin >> word;
    std::cout << "Word: " << word << "\n";
}


在上面,如果您按下EOF标记(注意:这可以从键盘生成),则将进入无限循环,或者在Unix系统中,可以将文件通过管道传输到输入流

为防止这种情况,请测试读取操作的结果。

operator>>返回对该流的引用(通常用于链接)。当在布尔上下文(如测试)中使用流时,使用operator bool将其转换为布尔值,该good()调用true,如果流仍处于良好状态(即读取操作未引起错误),则返回return 0;。 br />
第二次声明变量尽可能靠近使用点。

这是C ++的一个好习惯,因为对象具有可能很昂贵的构造函数(也包括析构函数) 。在函数顶部声明它们然后不使用它们是浪费的。

在声明使用点附近声明它们还可以在检查变量类型时允许局部读取。 >在您的情况下:

std::string word;
while(std::cin >> word && word != "STOP") {
    std::cout << "Word: " << word << "\n";
}


这是在顶部声明的,但仅在循环内部使用。

也不想在其中使用return 0应用程序从不返回错误状态时的main()。编译器将自动生成所需的q4312079q,并保留它为程序使用的标记,以指示程序永不失败。

为此,我将其重写为:

评论


\ $ \ begingroup \ $
将非常好的API设计默默地忽略...
\ $ \ endgroup \ $
–usr
17年11月20日在21:42

\ $ \ begingroup \ $
@usr设计使然。因此,当您到达要检查的地方时,可以验证它是否起作用。您当然可以将其行为更改为更加嘈杂(错误时引发异常),但是在处理用户输入时,这是不太理想的行为。
\ $ \ endgroup \ $
–马丁·约克
17年11月20日在22:02

\ $ \ begingroup \ $
通常,您需要立即使用读取结果。因此,您需要立即检查是否有错误。这个问题就是一个例子。异常非常适合于此。更改实例状态并需要查询对象以发现错误似乎是不必要的复杂情况。我们不是在谈论无效输入。我们正在谈论失败的IO操作。
\ $ \ endgroup \ $
–usr
17年11月21日在13:27

\ $ \ begingroup \ $
@usr这就是为什么我们不使用异常的原因(异常是一种处理用户错误的可怕机制)。异常是为以下情况而设计的:您不希望发生错误,并且正在将该错误传递给更高的上下文,由未知的系统管理员来处理。用户错误(是预期的),并因此在生成错误时进行处理,因此可以通过错误代码更好地进行处理。
\ $ \ endgroup \ $
–马丁·约克
17年11月21日在18:51



\ $ \ begingroup \ $
@usr我指向Sutters Mill,以阅读有关该主题的内容。我敢肯定,他有很多文章比我们在评论中能更全面地讨论该主题。
\ $ \ endgroup \ $
–马丁·约克
17年11月21日在18:54

#2 楼

对于初学者来说,这是非常好的代码。

几乎所有的C ++程序员都会犯错,那就是用::tolower参数调用char。进入德国变音符号或咖啡馆后,这将导致不确定的行为。您的程序可能看起来可以运行,但不能保证可以运行。关于tolower函数的任何不错的教程(顺便要求#include <ctype.h>)都应提及其参数必须强制转换为unsigned char。当您编写必须接受不受信任的数据的代码时。

下一个有用的步骤是将这些代码提取到其自己的函数中。想象一下,如果您已经可以通过提示符调用yesno,那么代码看起来会多么简单:

bool include_digits = yesno("Should the password include digits?");


评论


\ $ \ begingroup \ $
当我写以上文字时,我一定心情很好。现在,我再次查看代码,我发现我对代码未使用命名空间std印象深刻。该代码仍然有很多改进的余地。有关代码的改进版本,请参见codereview.stackexchange.com/q/180907。
\ $ \ endgroup \ $
–罗兰·伊利格(Roland Illig)
17年11月20日在21:34

#3 楼

我认为现有的评论涵盖了最重要的方面,但是作为补充,我想我将展示如何使用std::regex_match

#include <string>
#include <regex>
#include <iostream>

/* return 'y' if the answer matches the first regex,
 *        'n' if the answer matches the second regex,
 *        'q4312078q' if the answer matches neither
 */
char yesno(const std::string &answer, const std::regex &yes, const std::regex &no) {
    if (std::regex_match(answer, yes)) {
        return 'y';
    } else if (std::regex_match(answer, no)) {
        return 'n';
    }
    return 'q4312078q';
}

int main() {
#if ENGLISH
    static const std::regex yes{"y|yes", std::regex::icase | std::regex::optimize};
    static const std::regex no{"n|no", std::regex::icase | std::regex::optimize};
    static const std::string prompt{"Should the password include digits? [y/n] "};
#else
    static const std::regex yes{"j|ja", std::regex::icase | std::regex::optimize};
    static const std::regex no{"n|nein", std::regex::icase | std::regex::optimize};
    static const std::string prompt{"Soll das Passwort Ziffern enthalten? [j/n] "};
#endif
    for (char answer{'q4312078q'}; answer == 'q4312078q'; ) {
        std::string response;
        std::cout << prompt;
        std::cin >> response;
        if (std::cin) {
            answer = yesno(response, yes, no);
            std::cout << "Answer was \"" << answer << "\"\n";
        }
    }
}


看到,正则表达式并不难使用,并提供了很大的灵活性。在这种情况下,我已展示了如何支持英语或德语版本。

评论


\ $ \ begingroup \ $
我宁愿从L10N库中提取字符串也不愿对其进行硬编码。
\ $ \ endgroup \ $
–马丁·约克
17年11月21日在0:26

\ $ \ begingroup \ $
@LokiAstari当然,我也是真实代码的人,但这将掩盖我试图说明的观点,并且OP是一个自我描述的初学者。
\ $ \ endgroup \ $
–爱德华
17年11月21日在0:37

#4 楼

很多“花哨的”解决方案,但我还没有看到有人提到最简单的解决方案:只需检查第一个字符是y还是n
到较低的上方以简化更多操作...

基于下面@WesToleman的“ php”答案,您可以使用:

std::cout << "Should the password include digits? [Y/n] ";
    std::cin >> answer;
    answer_valid =
        (answer[0] == 'y') ||
        (answer[0] == 'n') ||
        (answer[0] == 'Y') ||
        (answer[0] == 'N');


您可以添加default: return false;,但我更喜欢这种方式。
answer[0]。

评论


\ $ \ begingroup \ $
不幸的是,这在Yowsers等输入上返回true!
\ $ \ endgroup \ $
– Wes Toleman
17年11月21日在1:39

\ $ \ begingroup \ $
是的-是的,这与原始问题以及yeS,yES之类的混合物(我不打算接受,但这是我的代码的结果)相符-正如我所说,这是简单的方法。
\ $ \ endgroup \ $
–John3136
17年11月21日在1:40

\ $ \ begingroup \ $
这是我想到的第一件事,而且我确定我已经使用了各种以这种方式工作的“适当”实用程序。您要让用户在问题中提供[y / n],因此输入以y开头的字符串被接受也就不足为奇了。在我看来,尝试匹配完整的特定字符串集对此有些过度设计。
\ $ \ endgroup \ $
–美元马特
17年11月21日在12:53

#5 楼

假设您有一个相当新的c ++编译器(c ++ 11或更高版本),则可以用更少的代码来完成此操作。此外,它还为您提供了更多的多功能性,您可以接受的答案: :

#include <iostream> 
#include <string>  
#include <set>

    int main()
    {

        std::string answer;

        std::set<std::string> validAnswers = {"Y", "y", "yes", "N", "n", "no", "si", "oui"};

        do
        {
            std::cout << "Should the password include digits? [Y/n] ";
        } while ((std::cin >> answer) && validAnswers.find(answer) == validAnswers.end());

        return 0;

    }


#6 楼

抱歉,C ++转换案例仅适用于原始类型。这将教会我在深夜用手机写答案的方法。该常数大于运行一个小的if或语句所花费的时间。

bool validWord(const std::string& word)
{
    const static std::unordered_set<std::string> validWords({ "yes", "no", "y", "n" });
    return validWords.count(word) == 1;
}