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