Search code examples
c++mathvectordoubleboost-spirit-x3

boost::spirit::x3 phrase_parse doing arithmetic operations before pushing into vector


I'm working on a project for my univertitiy studies. My goal is to read double numbers from a large file (2,6 GB) into a double vector.

I am working with the boost spirit x3 library with mmap. I have found some code in the net: https://github.com/sehe/bench_float_parsing which i am using.

Before pushing these double values into the vector i would like to do some arithmetic operations on these. So here i'm stuck. How can i do some artihmetic operations to double values before pushing them?

    template <typename source_it>
    size_t x3_phrase_parse<data::float_vector, source_it>::parse(source_it f, source_it l, data::float_vector& data) const {
        using namespace x3;
        bool ok = phrase_parse(f, l, *double_ % eol, space, data);
        if (ok)
            std::cout << "parse success\n";
        else
            std::cerr << "parse failed: '" << std::string(f, l) << "'\n";

        if (f != l) std::cerr << "trailing unparsed: '" << std::string(f, l) << "'\n";
        std::cout << "data.size(): " << data.size() << "\n";
        return data.size();
    }

Solution

  • I am sorry to not exactly answer your question. But boost spirit is not the appropriate tool. Spirit is a parser generator (as a subset is does of course also lexical analysis) . So, one level to high in the Chomsky hiearchy of languages. You do not need a parser but regular expressions: std:regex

    A double can easily be found with a regular expression. In the attached code, I created a simple pattern for a doubles. And a regex can be used to search for it.

    So, we will read from an istream (what can be a file, a stringstream, console input or whatever). We will read line by line, until the whole input is consumed.

    For each line, we will check, if the input matches the expected pattern, being 1 double.

    Then we read this double, do some calculations and then push it into the vector.

    Please see the following very simple code.

    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <string>
    #include <regex>
    
    std::istringstream input{R"(0.0
    1.5
    2.0
    3.0
    4.0
    -5.0
    )"};
    
    using VectorDouble = std::vector<double>;
    const std::regex reDouble{R"(([-+]?[0-9]*\.?[0-9]*))"};
    
    std::istream& get(std::istream& is, VectorDouble& dd)
    {
        // Reset vector to empty before reading
        dd.clear();
    
        //Read all data from istream
        std::string line{};
        while (getline(is, line)) {
            // Search for 2 doubles
            std::smatch sm;
            if (std::regex_search(line, sm, reDouble)) {
                // Convert found strings to double
                double d1{std::stod(sm[1])};
                // Do some calculations
                d1 = d1 + 10.0;
                // Push back into vector
                dd.emplace_back(d1);
            }
            else
                std::cerr << "Error found in line: " << line << "\n";
        }
        return is;
    }
    
    int main()
    {
        // Define vector and fill it
        VectorDouble dd{};
        (void)get(input, dd);
    
        // Some debug output
        for (double& d : dd) {
            std::cout << d << "\n";
        }
        return 0;
    }