CSV Parsing with c++

后端 未结 2 1512
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-20 07:57

I\'m trying to create a code that will parse through a csv database with stock information. Currently, I have the code generated so that it will search with a keyword and pr

2条回答
  •  佛祖请我去吃肉
    2020-12-20 08:30

    First a comma should be parsed as whitespace. You can do this by changing the internal std::ctype facet in the stream's locale:

    struct csv_classification : std::ctype {
        csv_classification() : ctype(make_table()) { }
    private:
        static mask* make_table() {
            const mask* classic = classic_table();
            static std::vector v(classic, classic + table_size);
            v[','] |= space;
            v[' '] &= ~space;
            return &v[0];
        }
    };
    

    Then set the locale using:

    ifs.imbue(std::locale(ifs.getloc(), new csv_classification));
    

    Next make a manipulator that checks to see if you're at the end of the line. If you are it sets the std::ios_base::failbit flag in the stream state. Also use internal storage to tell if the record belongs as a key or value in the map. Borrowing a bit from Dietmar...

    static int row_end = std::ios_base::xalloc();
    
    std::istream& record(std::istream& is) {
        while (std::isspace(is.peek())) {
            int c(is.peek());
            is.ignore();
    
            if (c == '\n') {
                is.iword(row_end) = !is.iword(row_end);
                is.setstate(std::ios_base::failbit);
            }
        }
        return is;
    }
    

    Then you can do:

    std::vector keys, values;
    
    for (std::string item;;) {
        if (ifs >> record >> item)
            keys.push_back(item);
        else if (ifs.eof())
            break;
        else if (ifs.iword(row_end)) {
            ifs.clear();
            while (ifs >> record >> item)
                values.push_back(item);
        }
        else
            break;
    }
    

    Now we need to apply both the keys and values and print them out. We can create a new algorithm for that:

    template
    void for_each_binary_range(Iter1 first1, Iter1 last1,
                               Iter2 first2, Iter2 last2, Function f)
    {
        assert(std::distance(first1, last1) <= std::distance(first2, last2));
    
        while (first1 != last1) {
            f(*first1++, *first2++);
        }
    }
    

    Finally we do:

    for_each_binary_range(std::begin(keys),   std::end(keys),
                          std::begin(values), std::end(values),
    [&] (std::string const& key, std::string const& value)
    {
        std::cout << key << ": " << value << std::endl;
    }
    

    Live Demo

提交回复
热议问题