使用字符串流逐行读取输入文件

时间:2017-03-23 17:09:10

标签: c++ stringstream read-write

我有一个数据文件“records.txt”,其格式如下:

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

using namespace std;

const string record1("records.txt");

// declaring a struct for each record
struct record
{
    int number;             // number of record
    vector<int> content;    // content of record    
};

int main()
{
    record batch_1;         // stores integers from 1 - 64
    record batch_2;         // stores integers from 65 - 128
    record temp;
    string line;

    // read the data file
    ifstream read_record1(record1.c_str());
    if (read_record1.fail()) 
    {
        cerr << "Cannot open " << record1 << endl;
        exit(EXIT_FAILURE);
    } 
    else
        cout << "Reading data file: " << record1 << endl;

    cout << "Starting Batch 1..." << endl;
    read_record1.open(record1.c_str());
    while(getline(read_record1, line))
    {       
        stringstream S;
        S << line;              // store the line just read into the string stream
        vector<int> thisLine;   // save the numbers read into a vector
        for (int c = 0; c < 33; c++)    // WE KNOW THERE WILL BE 33 ENTRIES
        {
            S >> thisLine[c];
            cout << thisLine[c] << " ";
        }
        for (int d = 0; d < thisLine.size(); d++) 
        {
            if (d == 0)
                temp.number = thisLine[d];
            else
                temp.content.push_back(thisLine[d]);
            cout << temp.content[d] << " ";
        }   

        if (temp.number == 1) 
        {
            batch_1.content = temp.content;
            temp.content.clear();
        }

        thisLine.clear();
    }

    // DUPLICATE ABOVE FOR BATCH TWO

    return 0;
}   

每行以一个或两个开头,表示它属于哪个批次。我试图使用字符串流来读取每一行并将结果存储在结构中,第一个数字对应于批号,后面的32个整数对应于内容,属于结构向量。我一直在努力与此斗争,我按照这里找到的解决方案:How to read line by line

结果程序如下:

Starting Batch 1...

程序编译并以返回值0运行,但循环中的cout语句不执行,因为唯一的控制台输出是:

{{1}}

此外,如果代码重复批次2,我会遇到分段错误。很明显,这是行不通的。我不太熟悉阅读字符串,所以任何帮助都会受到赞赏。另外,如果行没有等量的条目(例如,一行有33个条目,另一行有15个条目),我该怎么办?

2 个答案:

答案 0 :(得分:1)

您的代码存在许多问题:

  1. 您正在打开输入文件两次。没什么大不了的,但也不可取。如果您将文件名传递给std::ifstream构造函数,它会立即打开文件,因此之后无需再调用open()

  2. for循环的第一个while循环内,您尝试使用thisLine直接将整数读取到本地operator>>向量中,但这样做会因为你还没有为thisLine的数组分配任何内存,所以无法正常工作。由于您需要33个整数,因此可以在读取之前预先分配数组:

    vector<int> thisLine(33);
    

    或者:

    vector<int> thisLine;
    thisLine.resize(33);
    

    但是,既然你也问过单独行有不同整数的可能性,你根本不应该预先调整向量的大小,因为你还不知道整数的数量(虽然你可以预先分配如果你知道你可能期望的最大整数数,那么向量的容量。您可以使用while循环而不是for循环,这样您就可以读取整个std::stringstream,无论它实际拥有多少整数:

    thisLine.reserve(33); // optional
    
    int c;
    while (S >> c) {
        thisLine.push_back(c);
    }
    
  3. 在第二个for循环中,您正在访问temp.content[d],但如果d为0,那么temp.content可能尚未填充,因此访问{ {1}}无效(如果您使用了temp.content[0],则会遇到temp.content.at(d)例外)。你可能想做更像这样的事情:

    std::out_of_range

    但即使这样也可以通过完全删除for (int d = 0; d < thisLine.size(); d++) { if (d == 0) temp.number = thisLine[d]; else { temp.content.push_back(thisLine[d]); cout << thisLine[d] << " "; } } 循环来简化:

    push_back()
  4. 您循环遍历整个文件一次,读取所有记录但只处理批处理1记录。您说您有一组重复的循环来处理批次2记录。这意味着您将再次重新读取整个文件,重新读取所有记录但忽略批处理1记录。这是很多浪费的开销。您应该读取文件一次,根据需要分离批次,然后在读取循环结束时处理它们,例如:

    if (thisLine.size() > 0)
    {
        temp.number = thisLine[0];
        thisLine.erase(thisLine.begin());
    }
    temp.content = thisLine;
    
    for (int d = 0; d < thisLine.size(); d++) 
        cout << thisLine[d] << " ";
    
  5. 因此,如上所述,更正后的代码应该更像这样:

    vector<record> batch_1;         // stores integers from 1 - 64
    vector<record> batch_2;         // stores integers from 65 - 128
    record temp;
    
    ...
    
    while(getline(read_record1, line))
    {       
        ...
        if (temp.number == 1) {
            batch_1.push_back(temp);
        } else {
            batch_2.push_back(temp);
        }
    }
    
    // process batch_1 and batch_2 as needed...
    

    然后你可以通过完全摆脱#include <iostream> #include <fstream> #include <sstream> #include <string> #include <vector> using namespace std; const string records_file("records.txt"); // declaring a struct for each record struct record { int number; // number of record vector<int> content; // content of record }; int main() { vector<record> batch_1; // stores integers from 1 - 64 vector<record> batch_2; // stores integers from 65 - 128 record temp; string line; // read the data file ifstream read_records(records_file.c_str()); if (read_records.fail()) { cerr << "Cannot open " << records_file << endl; exit(EXIT_FAILURE); } cout << "Reading data file: " << records_file << endl; cout << "Starting Batch 1..." << endl; while (getline(read_records, line)) { istringstream S(line); // store the line just read into the string stream vector<int> thisLine; // save the numbers read into a vector thisLine.reserve(33); // WE KNOW THERE WILL BE 33 ENTRIES int c; while (S >> c) { thisLine.push_back(c); cout << c << " "; } temp.number = 0; temp.content.reserve(thisLine.size()); for (int d = 0; d < thisLine.size(); d++) { if (d == 0) temp.number = thisLine[d]; else temp.content.push_back(thisLine[d]); } /* alternatively: if (thisLine.size() > 0) { temp.number = thisLine[0]; thisLine.erase(thisLine.begin()); } temp.content = thisLine; */ if (temp.number == 1) { batch_1.push_back(temp); } temp.content.clear(); } read_records.seekg(0); cout << "Starting Batch 2..." << endl; // DUPLICATE ABOVE FOR BATCH TWO read_records.close(); // process batch_1 qand batch_2 as needed... return 0; } 向量来简化你的阅读循环:

    thisLine

    然后,如果您如此倾向,可以使用std::copy()代替std::istream_iteratorstd::back_insertor来进一步简化代码:

    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    const string records_file("records.txt");
    
    // declaring a struct for each record
    struct record
    {
        int number;             // number of record
        vector<int> content;    // content of record    
    };
    
    int main()
    {
        vector<record> batch_1;         // stores integers from 1 - 64
        vector<record> batch_2;         // stores integers from 65 - 128
        record temp;
        string line;
    
        // read the data file
        ifstream read_records(records_file.c_str());
        if (read_records.fail()) 
        {
            cerr << "Cannot open " << records_file << endl;
            exit(EXIT_FAILURE);
        } 
    
        cout << "Reading data file: " << records_file << endl;
    
        cout << "Starting Batch 1..." << endl;
    
        while (getline(read_records, line))
        {       
            istringstream S(line);  // store the line just read into the string stream
            if (S >> temp.number)
            {
                cout << temp.number << " ";
    
                temp.content.reserve(32);   // WE KNOW THERE WILL BE 32 ENTRIES
    
                int c;
                while (S >> c) {
                    temp.content.push_back(c);
                    cout << c << " ";
                }
    
                if (temp.number == 1) {
                    batch_1.push_back(temp);
                }
    
                temp.content.clear();
            }
        }
    
        read_records.seekg(0);
    
        cout << "Starting Batch 2..." << endl;
    
        // DUPLICATE ABOVE FOR BATCH TWO
    
        read_records.close();
    
        // process batch_1 qand batch_2 as needed...
    
        return 0;
    }
    

答案 1 :(得分:-4)

您是否查找了std::getline的{​​{3}}?我不相信它会像您似乎假设的那样返回bool。看起来它会返回std::istream&。惊讶它编译,必须做一些奇怪的隐式转换。