Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parse a file using C++, load the value to a structure

Tags:

c++

file

parsing

I have the following file/line:

pc=1 ct=1 av=112 cv=1100 cp=1700 rec=2 p=10001 g=0 a=0 sz=5 cr=200
pc=1 ct=1 av=113 cv=1110 cp=1800 rec=2 p=10001 g=0 a=10 sz=5 cr=200

and so on. I wish to parse this and take the key value pairs and put them in a structure:

struct pky
{
    pky() :
      a_id(0),
      sz_id(0),
      cr_id(0),
      cp_id(0),
      cv_id(0),
      ct_id(0),
      fr(0),
      g('U'),
      a(0),
      pc(0),
      p_id(0)
    { }
};

wherein either all the structure fields are used or some might be omitted.

How do I create a C++ class, which will do the same? I am new to C++ and not aware of any functions or library which would do this work.

Each line is to be processed, and the structure will be populated with one line each time and used, before it is flushed. The structure is later used as a parameter to a function.

like image 996
gagneet Avatar asked Dec 02 '22 08:12

gagneet


2 Answers

You can do something like this:

std::string line;
std::map<std::string, std::string> props;
std::ifstream file("foo.txt");
while(std::getline(file, line)) {
    std::string token;
    std::istringstream tokens(line);
    while(tokens >> token) {
        std::size_t pos = token.find('=');
        if(pos != std::string::npos) {
            props[token.substr(0, pos)] = token.substr(pos + 1);
        }
    }

    /* work with those keys/values by doing properties["name"] */
    Line l(props["pc"], props["ct"], ...);

    /* clear the map for the next line */
    props.clear();
}

i hope it's helpful. Line can be like this:

struct Line { 
    std::string pc, ct; 
    Line(std::string const& pc, std::string const& ct):pc(pc), ct(ct) {

    }
};

now that works only if the delimiter is a space. you can make it work with other delimiters too. change

while(tokens >> token) {

into for example the following, if you want to have a semicolon:

while(std::getline(tokens, token, ';')) {

actually, it looks like you have only integers as values, and whitespace as delimiters. you might want to change

    std::string token;
    std::istringstream tokens(line);
    while(tokens >> token) {
        std::size_t pos = token.find('=');
        if(pos != std::string::npos) {
            props[token.substr(0, pos)] = token.substr(pos + 1);
        }
    }

into this then:

    int value;
    std::string key;
    std::istringstream tokens(line);
    while(tokens >> std::ws && std::getline(tokens, key, '=') && 
          tokens >> std::ws >> value) {
            props[key] = value;
    }

std::ws just eats whitespace. you should change the type of props to

std::map<std::string, int> props;

then too, and make Line accept int instead of std::string's. i hope this is not too much information at once.

like image 179
Johannes Schaub - litb Avatar answered Dec 07 '22 23:12

Johannes Schaub - litb


This is the perfect place to define the stream operators for your structure:

#include <string>
#include <fstream>
#include <sstream>
#include <istream>
#include <vector>
#include <algorithm>
#include <iterator>

std::istream& operator>> (std::istream& str,pky& value)
{
    std::string line;
    std::getline(str,line);

    std::stringstream dataStr(line);

    static const std::streamsize max = std::numeric_limits<std::streamsize>::max();

    // Code assumes the ordering is always as follows
    // pc=1 ct=1 av=112 cv=1100 cp=1700 rec=2 p=10001 g=0 a=0 sz=5 cr=200
    dataStr.ignore(max,'=') >> value.pc;
    dataStr.ignore(max,'=') >> value.ct_id;
    dataStr.ignore(max,'=') >> value.a; // Guessing av=
    dataStr.ignore(max,'=') >> value.cv_id;
    dataStr.ignore(max,'=') >> value.cp_id;
    dataStr.ignore(max,'=') >> value.fr; // Guessing rec=
    dataStr.ignore(max,'=') >> value.p_id;
    dataStr.ignore(max,'=') >> value.g;
    dataStr.ignore(max,'=') >> value.a_id;
    dataStr.ignore(max,'=') >> value.sz_id;
    dataStr.ignore(max,'=') >> value.cr_id;

    return str;
}

int main()
{
    std::ifstream  file("plop");

    std::vector<pky>  v;
    pky data;

    while(file >> data)
    {
        // Do Somthing with data
        v.push_back(data);
    }

    // Even use the istream_iterators
    std::ifstream    file2("plop2");
    std::vector<pky> v2;

    std::copy(std::istream_iterator<pky>(file2),
              std::istream_iterator<pky>(),
              std::back_inserter(v2)
             );
}
like image 26
Martin York Avatar answered Dec 07 '22 22:12

Martin York