简体   繁体   中英

Extracting numbers from iterator

In this SO question, there is a brilliant answer due to @HowardHinnant that extracts numbers from a text file into a vector very efficiently. Here is the code for brevity:

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

// g++ -std=c++11 test.cpp -o t

int main()
{
    std::ifstream theStream("data.txt"); 
    if( ! theStream )
          std::cerr << "data.txt\n";
    while (true)
    {
        std::string line;
        std::getline(theStream, line);
        if (line.empty())
            break;

        std::istringstream myStream( line );
        std::istream_iterator<int> begin(myStream), eof;
        std::vector<int> numbers(begin, eof);

        // process line however you need
        std::copy(numbers.begin(), numbers.end(),
                  std::ostream_iterator<int>(std::cout, " "));
        std::cout << '\n';
    }
}

A sample of the data would be

800 170 84 439 
129 902 103 492 394 140 496 893 
229 645 
164 389 74 208 726 315 
291 421 230 789 246 791 762 
416 241 538 810 605 
714 555 54 863 
288 465 563 831 
0 339 740 427 718 
449 675 545 842 779 607 
274 958 

I have studied this code for two days now, and can't quite grasp how to extract the numbers from each line. So, how can I access, lets say, the 3rd number on the second row (103), or more generally, the nth number on the mth row?

Any explanation of how this solution works would really help too!

Use a vector of vectors to store each lines numbers seperately:

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

int main()
{
    std::ifstream theStream("data.txt");
    if (!theStream)
        std::cerr << "data.txt\n";

    std::vector<std::vector<int>> data;  // vector to hold the numbers of each line seperately
    while (true)
    {
        std::string line;
        std::getline(theStream, line);
        if (line.empty())
            break;

        std::istringstream myStream(line);
        std::istream_iterator<int> begin(myStream), eof;
        std::vector<int> numbers(begin, eof);

        // process line however you need
        data.push_back(numbers); // add numbers of current line to data
    }

    std::cout << data[1][2] << '\n'; // 2nd row, 3rd number: 103
}

Assuming the query will only be made once, you can do it in one pass by counting which row and column you're on while you're extracting (and with each iteration of the while loop); then break from there. This is so that you don't have to read the entire file.

Which might look like:

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

int main()
{
    std::ifstream theStream("data.txt");
    if (!theStream)
        std::cerr << "data.txt\n";

    std::size_t target_row = 2;  // 1-based
    std::size_t target_col = 3;  // 1-based

    int value = 0;
    int valid = false;

    for (std::size_t current_row = 1; true; ++current_row)
    {
        std::string line;
        if (!std::getline(theStream, line) || line.empty())
            break;

        if (current_row != target_row)
            continue;

        std::istringstream myStream(line);
        for (std::size_t current_col = 1; myStream >> value; ++current_col)
            if (current_col == target_col) {
                valid = true;
                break;
            }           

        break;
    }

    if (!valid)
        std::cerr << "No such row and column!\n\n";
    else
        std::cout << value;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM