C++ 如何测试 stringstream operator>> 是否已解析错误类型并跳过它
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24504582/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to test whether stringstream operator>> has parsed a bad type and skip it
提问by Fantastic Mr Fox
I am interested in discussing methods for using stringstream
to parse a line with multiple types. I would begin by looking at the following line:
我有兴趣讨论用于stringstream
解析具有多种类型的行的方法。我将首先查看以下行:
"2.832 1.3067 nana 1.678"
Now lets assume I have a long line that has multiple strings
and doubles
. The obvious way to solve this is to tokenize the string and then check converting each one. I am interested in skipping this second step and using stringstream
directly to only find the numbers.
现在让我们假设我有一条长线,其中包含多个strings
和doubles
。解决这个问题的显而易见的方法是对字符串进行标记,然后检查每个字符串的转换。我有兴趣跳过这第二步并stringstream
直接使用仅查找数字。
I figured a good way to approach this would be to read through the string and check if the failbit
has been set, which it will if I try to parse a string into a double.
我想一个解决这个问题的好方法是通读字符串并检查是否failbit
已设置,如果我尝试将字符串解析为双精度值,它会这样做。
Say I have the following code:
假设我有以下代码:
string a("2.832 1.3067 nana 1.678");
stringstream parser;
parser.str(a);
for (int i = 0; i < 4; ++i)
{
double b;
parser >> b;
if (parser.fail())
{
std::cout << "Failed!" << std::endl;
parser.clear();
}
std::cout << b << std::endl;
}
It will print out the following:
它将打印出以下内容:
2.832
1.3067
Failed!
0
Failed!
0
I am not surprised that it fails to parse a string, but what is happening internally such that it fails to clear its failbit
and parse the next number?
我对它无法解析字符串并不感到惊讶,但是内部发生了什么以至于它无法清除它failbit
并解析下一个数字?
采纳答案by π?ντα ?ε?
The following code works well to skip the bad wordand collect the valid double
values
下面的代码工作很好地跳过坏词,收集有效double
值
istringstream iss("2.832 1.3067 nana 1.678");
double num = 0;
while(iss >> num || !iss.eof()) {
if(iss.fail()) {
iss.clear();
string dummy;
iss >> dummy;
continue;
}
cout << num << endl;
}
Here's a fully working sample.
这是一个完整的工作示例。
Your sample almost got it right, it was just missing to consume the invalid input field from the stream after detecting it's wrong format
您的示例几乎是正确的,只是在检测到格式错误后无法使用流中的无效输入字段
if (parser.fail()) {
std::cout << "Failed!" << std::endl;
parser.clear();
string dummy;
parser >> dummy;
}
In your case the extraction will try to read again from "nana"
for the last iteration, hence the last two lines in the output.
在您的情况下,提取将尝试从"nana"
最后一次迭代中再次读取,因此输出中的最后两行。
Also note the trickery about iostream::fail()
and how to actually test for iostream::eof()
in my 1st sample. There's a well known Q&A, why simple testing for EOF as a loop condition is considered wrong. And it answers well, how to break the input loop when unexpected/invalid values were encountered. But just how to skip/ignore invalid input fields isn't explained there (and wasn't asked for).
还要注意我的第一个样本中的技巧iostream::fail()
以及如何实际测试iostream::eof()
。有一个众所周知的问答,为什么将 EOF 作为循环条件的简单测试被认为是错误的。它很好地回答了如何在遇到意外/无效值时中断输入循环。但是那里没有解释如何跳过/忽略无效的输入字段(也没有被要求)。
回答by π?ντα ?ε?
I have built up a more fine tuned version for this, that is able to skip invalid input character wise (without need to separate double
numbers with whitespace characters):
我为此建立了一个更精细调整的版本,它能够跳过无效的输入字符(无需double
用空格字符分隔数字):
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
istringstream iss("2.832 1.3067 nana1.678 xxx.05 meh.ugh");
double num = 0;
while(iss >> num || !iss.eof()) {
if(iss.fail()) {
iss.clear();
while(iss) {
char dummy = iss.peek();
if(std::isdigit(dummy) || dummy == '.') {
// Stop consuming invalid double characters
break;
}
else {
iss >> dummy; // Consume invalid double characters
}
}
continue;
}
cout << num << endl;
}
return 0;
}
Output
输出
2.832
1.3067
1.678
0.05
回答by Tony Delroy
Few minor differences to π?ντα ?ε?'s answer - makes it also handle e.g. negative number representations etc., as well as being - IMHO - a little simpler to read.
π?ντα ?ε? 的答案几乎没有细微差别-使其还可以处理例如负数表示等,以及 - 恕我直言 - 更容易阅读。
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
double num = 0;
for (; iss; )
if (iss >> num)
std::cout << num << '\n';
else if (!iss.eof())
{
iss.clear();
iss.ignore(1);
}
}
Output:
输出:
2.832
1.3067
1.678
-100
0.05
(see it running here)
(看到它在这里运行)
回答by Tony Delroy
If you like concision - here's another option that (ab?)uses &&
to get cout
done only when a number's been parsed successfully, and when a number isn't parsed it uses the comma operator to be able to clear()
stream error state inside the conditional before reading a character to be ignored...
如果你喜欢简洁-这里的另一个选项,(?AB)使用&&
即可cout
完成,只有当许多的成功解析,而当数量不解析它使用逗号操作员能够clear()
读取之前流条件中的错误状态一个被忽略的字符...
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::istringstream iss("2.832 1.3067 nana1.678 x-1E2 xxx.05 meh.ugh");
double num = 0;
char ignored;
while (iss >> num && std::cout << num << '\n' ||
(iss.clear(), iss) >> ignored)
;
}
回答by Galik
You can use std::istringstream::eof()
to validateinput like this:
您可以使用std::istringstream::eof()
来验证输入这样的:
#include <string>
#include <sstream>
#include <iostream>
// remove white-space from each end of a std::string
inline std::string& trim(std::string& s, const char* t = " \t")
{
s.erase(s.find_last_not_of(t) + 1);
s.erase(0, s.find_first_not_of(t));
return s;
}
// serial input
std::istringstream in1(R"~(
2.34 3 3.f 3.d .75 0 wibble
)~");
// line input
std::istringstream in2(R"~(
2.34
3
3.f
3.d
.75
0
wibble
)~");
int main()
{
std::string input;
// NOTE: This technique will not work if input is empty
// or contains only white-space characters. Therefore
// it is safe to use after a conditional extraction
// operation >> but it is not reliable after std::getline()
// without further checks.
while(in1 >> input)
{
// input will not be empty and will not contain white-space.
double d;
if((std::istringstream(input) >> d >> std::ws).eof())
{
// d is a valid double
std::cout << "d1: " << d << '\n';
}
}
std::cout << '\n';
while(std::getline(in2, input))
{
// eliminate blank lines and lines
// containing only white-space (trim())
if(trim(input).empty())
continue;
// NOW this is safe to use
double d;
if((std::istringstream(input) >> d >> std::ws).eof())
{
// d is a valid double
std::cout << "d2: " << d << '\n';
}
}
}
This works because the eof()
check ensures that onlythe double was entered and not garbage like 12d4
.
这是有效的,因为eof()
检查确保只输入了 double 而不是像12d4
.