7

我正在尝试使用 stringstream 拆分字符串:

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main(){
    ifstream fp;
    string name;

    fp.open("in.txt");
    while (fp){
        string line;
        getline(fp, line);
        cout << line << endl;
        istringstream line_stream(line);

        while (line_stream){
            line_stream >> name;
            cout << name << " ";
        }
    }

    return 0;
}

这是 in.txt :

cat bat rat sat

这是我得到的输出:

cat bat rat sat
cat bat rat sat sat

getline()函数中检索到的行是正确的,但是在拆分过程中,我得到了最后一个单词两次。我不确定为什么会这样。

4

4 回答 4

7

您正在使用结果getline而不检查它是否成功。这是第一个错误(可能会导致您显示的代码出现额外的空行)。同样,你使用的结果line_stream >> name不检查是否成功;在这种情况下(因为name不是每次都新建),您最终可能会得到以前读取的值(但在这两种情况下,字符串的内容都是未指定的)。

在没有首先测试它是否成功之前,你绝不能使用输入的结果。最常见的方法(但肯定不是唯一的方法)是在循环条件下进行输入:

while ( std::getline( fp, line ) ) ...

while ( line_stream >> name ) ...

如果您仍想将变量的范围限制为循环,则必须编写:

while ( fp ) {
    std::string line;
    if ( std::getline( fp, line ) ) {
        //  rest of loop
    }
}

如果您(可以理解)反对在某个条件下修改全局状态,则必须编写:

std::getline( fp, line );
while ( fp ) {
    //  ...
    std::getline( fp, line );
}

虽然我认为有强有力的论据支持这一点,但这个while ( std::getline( fp, line ) )成语无处不在,以至于其他任何事情都会让读者想知道为什么。

于 2013-07-23T10:50:01.290 回答
1

而不是说:

while (fp){
    string line;
    getline(fp, line);
    ...

你应该说:

string line;
while(getline(fp, line)) { ...

这是因为当 fp 进入 eof 状态时,getline 失败(并将 fp 设置为 eof 状态)。您不检查 getline 的结果,因此对于最后一步,您使用先前读取的值。

于 2013-07-23T10:34:47.840 回答
1

问题在于循环。一旦它从文件中读取最后一个单词并再次进入循环,该条件就不会失败,它会再次进入循环。

将两个while循环更改为

while(getline(fp, line))

&

while (line_stream >> name)

这将防止重复,因为在这种情况下,lastname不会被处理两次。目前,当fp接近时eofgetline失败。getline不检查并且使用先前读取的值,而不检查它是否成功。

于 2013-07-23T10:35:19.153 回答
1

除了通过检查 的结果来检测文件结尾的建议更改外getline,同样的原则应该适用于stringstream,因此:

while (line_stream){

应该:

while (line_stream >> name) { 

这样,当“没有什么可以改名”时,您不会得到最后一个name处理两次。

于 2013-07-23T10:38:22.547 回答