我如何检查流提取已消耗所有输入
How do I check that stream extraction has consumed all input?
在以下功能中,我尝试查看字符串s
是否可以通过查看是否可以读取类型T
来转换为T
类型,以及之后是否完全消耗了输入。我想要
template <class T>
bool can_be_converted_to(const std::string& s, T& t)
{
std::istringstream i(s);
i>>std::boolalpha;
i>>t;
if (i and i.eof())
return true;
else
return false;
}
但是,can_be_converted_to<bool>("true")
评估为false,因为i.eof()
在功能结束时为false。
这是正确的,即使该功能已经读取了整个字符串,因为它没有尝试读取 paste 字符串的末尾。(因此,显然,此功能适用于INT和双重,因为istringstream
在阅读这些功能时读取了这些功能。)
所以,假设我确实应该检查(i and <input completely consumed>)
:
问:如何使用EOF()?
使用peek()
或get()
检查流中的下一步:
return (i >> std::boolalpha >> t && i.peek() == EOF);
您的版本也不适用于整数。考虑此输入:123 45
。它将读取123并报告True,即使流中剩下一些字符。
在标准库的许多实现中,eof
才能在尝试阅读超过末尾后才设置。您可以通过执行:
char _;
if (i && !(i >> _)) { // i is in a valid state, but
// reading a single extra char fails
在jrok的答案上扩展,您可以轻松地使用i.get()
i.peek()
,至少在这种情况下。(我不知道是否有任何理由更喜欢一个。)
另外,遵循惯例,即白空间绝不只是一个分离器,您可能需要在检查结束之前提取它。类似:
return i >> std::ws && i.get() == std::istream::traits_type::eof();
std::ws
的一些较旧的实现是错误的,会把以错误状态流。在这种情况下,您必须倒数测试,并做类似:
return !(i >> std::ws) || i.get() == std::istream::traits_type::eof();
或只是在情况前读取std::ws
,并独特地依赖i.get()
。
(我不知道Buggy std::ws
仍然是一个问题。我开发了一个它在当时工作的版本,我刚刚继续使用它。)
我想提供一种完全不同的方法:取您的输入字符串,自己象征性,然后使用boost::lexical_cast<T>
转换单个字段。
原因:我浪费了一个下午,解析了一个包含2个int和2个双场的弦,并被空间隔开。执行以下操作:
int i, j;
double x, y;
std::istringstream ins{str};
ins >> i >> j >> x >> y;
// how to check errors???...
解析正确的输入,例如
`"5 3 9.9e+01 5.5e+02"`
正确,但没有检测到此问题:
`"5 9.6e+01 5.5e+02"`
发生的情况是i
将设置为5(确定),j
将设置为9(??),x
至6.0(= 0.6E 01),y
至550(OK)。看到未设置failbit
我感到非常惊讶...(平台信息:OS X 10.9,Apple Clang 6.0,C 11模式)。
当然,您现在可以说:"但是,等等,标准规定应该是这样的",您可能是对的,但是知道这是一个功能而不是错误,如果您想减轻痛苦在不编写数英里代码的情况下进行正确检查的正确错误。
otoh,如果您使用" Marius"的出色令牌功能并首先在Whitespace上拆分str
,那么突然,一切都变得非常容易。这是Tokeniser的一个稍微修改的版本。我重新编写了它以返回一个字符串的向量;原始模板是将令牌放在带有元件的容器中,可转换为字符串。(对于那些需要这种通用方法的人,请咨询上面的原始链接。)
// param str: the input string to be tokenized
// param delimiters: string of delimiter characters
// param trimEmpty: if true then empty tokens will be trimmed
// return a vector of strings containing the tokens
std::vector<std::string> tokenizer(
const std::string& str,
const std::string& delimiters = " ",
const bool trimEmpty = false
) {
std::vector<std::string> tokens;
std::string::size_type pos, lastPos = 0;
const char* strdata = str.data();
while(true) {
pos = str.find_first_of(delimiters, lastPos);
if(pos == std::string::npos) {
// no more delimiters
pos = str.length();
if(pos != lastPos || !trimEmpty) {
tokens.emplace_back(strdata + lastPos, pos - lastPos);
}
break;
} else {
if(pos != lastPos || !trimEmpty) {
tokens.emplace_back(strdata + lastPos, pos - lastPos);
}
}
lastPos = pos + 1;
}
return tokens;
}
然后像这样使用它(ParseError
是一些异常对象):
std::vector<std::string> tokens = tokenizer(str, " t", true);
if (tokens.size() < 4)
throw ParseError{"Too few fields in " + str};
try {
unsigned int i{ boost::lexical_cast<unsigned int>(tokens[0]) },
j{ boost::lexical_cast<unsigned int>(tokens[1]) };
double x{ boost::lexical_cast<double>(tokens[2]) },
y{ boost::lexical_cast<double>(tokens[3]) };
// print or process i, j, x, y ...
} catch(const boost::bad_lexical_cast& error) {
throw ParseError{"Could not parse " + str};
}
注意:如果愿意,您可以使用Boost Split或Dokenizer,但它们比Marius的Tokeniser慢(至少在我的环境中)。
更新:代替boost::lexical_cast<T>
,您可以使用C 11" std::sto*
"函数(例如stoi
将字符串令牌转换为INT)。这些抛出两种例外:std::invalid_argument
如果无法执行转换,并且如果无法表示转换值,则std::out_of_range
。您可以单独捕获这些或他们的父级std::runtime_error
。对上面的示例代码进行修改作为练习给读者: - )
- 清除输入缓冲区后未提取字符串流
- 如何从输入字符串中提取特定位置的字符?
- 使用非成员函数从输入流中提取类对象
- 接受多个输入(如 +、- 和平方数字)的计算器.从文本文件中提取信息
- 从 std::copy 和 std::copy_n 中提取输入迭代器
- 如何使用 cin 从输入的字符串中提取特定的数值
- 如何从输入中提取
- 从用户输入中提取整数的枚举开关语句
- 从输入文件 c++ 中提取和设置变量
- 输出文件从输入中提取零
- 是否可以使用一行代码从 std::cin 中提取格式化输入
- 我如何检查流提取已消耗所有输入
- 从文本文件中读取输入并将其提取 - C++
- 读取输入流而不提取
- 从指向字符数组的指针中提取输入
- 从输入字符串 C++ 中提取数字
- 从未知长度的手动输入中提取单个单词
- 使用getline(),然后从用户输入中提取字符串(使用c++)
- 如何从输入字符串提取数值?c++
- 从输入图像中提取色调范围