0

If I wanted to parse from a file line-by-line and store the data in separate variables, using getline to store into a string and parse/convert to separate variables seems like a long way to do it. For example, for line "ABC a1 1 2 3", if I wanted to store the first two data into strings and the rest of the three into integers, what would be an efficient way to read from the file line-by-line and store it accordingly?

Joe
  • 7
  • 5
  • 1
    C++ does not have a reputation of having a bunch of shortcuts, that make things fast and easy. Using `std::getline()`, then parsing each line, is the most hassle-free, and simplest approach. stackoverflow.com is littered with endless,flaming wreckage that results from attempting to use the formatted extraction `>>` operator to deal with line-oriented input. Don't do it. `std::getline()` is your friend. Learn it. Love it. – Sam Varshavchik Dec 04 '17 at 02:55
  • Honestly in my mind I think you should be storing in a `vector` then use this to parse the individual `string`s: https://stackoverflow.com/q/32991193/2642059 but because of the vagueness of the question I can't guarantee this is the right answer. – Jonathan Mee Dec 04 '17 at 03:55
  • if every line have same representation, `while(cin >> s1 >> s2 >> i1 >> i2 >> i3) save values;` is a way. – apple apple Dec 04 '17 at 04:00
  • The above method worked the best. Thanks – Joe Dec 11 '17 at 19:13

1 Answers1

0

Boost.Spirit is suited for parsing custom data formats with your own grammar.

#include <iostream>
#include <sstream>
#include <string>
#include <tuple>

#include <boost/fusion/include/std_tuple.hpp>
#include <boost/spirit/home/x3.hpp>

std::tuple<std::string, std::string, int, int, int>
parse(std::string const &input)
{
    auto iter = input.begin();

    using namespace boost::spirit::x3;

    auto name = rule<class name, std::string>{}
        = lexeme[upper >> *upper];
    auto ident = rule<class ident, std::string>{}
        = lexeme[alpha >> *alnum];

    std::tuple<std::string, std::string, int, int, int> result;

    bool r = phrase_parse(iter, input.end(),
                          name > ident > int_ > int_ > int_,
                          space, result);

    if (!r || iter != input.end())
    {
        std::string rest(iter, input.end());
        throw std::invalid_argument("Parsing failed at " + rest);
    }

    return result;
}


int main()
{
    // This could be a file instead with std::ifstream
    std::istringstream input;
    input.str("ABC a1 1 2 3\nDEF b2 4 5 6\n");

    for (std::string line; std::getline(input, line); )
    {
        std::string name, ident;
        int a, b, c;
        std::tie(name,ident,a,b,c) = parse(line);

        std::cout << "Found the following entries:\n"
                  << "Name: " << name << "\n"
                  << "Identifier: " << ident << "\n"
                  << "a: " << a << "\n"
                  << "b: " << b << "\n"
                  << "c: " << c << "\n";
    }
}

Live example

Henri Menke
  • 10,705
  • 1
  • 24
  • 42