Разбор файла с использованием C ++, загрузка значения в структуру - PullRequest
3 голосов
/ 07 ноября 2008

У меня есть следующий файл / строка:

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

и так далее. Я хочу разобрать это и взять пары ключ-значение и поместить их в структуру:

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)
    { }
};

где либо используются все поля структуры, либо некоторые могут быть опущены.

Как мне создать класс C ++, который будет делать то же самое? Я новичок в C ++ и не знаю каких-либо функций или библиотек, которые могли бы сделать эту работу.

Каждая строка должна быть обработана, и структура будет заполняться одной строкой каждый раз и использоваться до ее очистки. Позднее эта структура используется в качестве параметра функции.

Ответы [ 5 ]

10 голосов
/ 07 ноября 2008

Вы можете сделать что-то вроде этого:

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();
}

Я надеюсь, что это полезно. Строка может быть такой:

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

    }
};

теперь это работает, только если разделитель - пробел. Вы также можете заставить его работать с другими разделителями. изменить

while(tokens >> token) {

Например, если вы хотите использовать точку с запятой:

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

на самом деле, похоже, у вас есть только целые числа в качестве значений и пробелы в качестве разделителей. Вы можете изменить

    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);
        }
    }

в это тогда:

    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 просто ест пробел. Вы должны изменить тип реквизита на

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

тогда тоже, и заставьте Line принимать int вместо std :: string's. Я надеюсь, что это не слишком много информации сразу.

4 голосов
/ 07 ноября 2008

Это идеальное место для определения операторов потока для вашей структуры:

#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)
             );
}
2 голосов
/ 07 ноября 2008

Похоже, это сработало. Конечно, вы извлечете код, который я написал в main, и вставите его в класс или что-то еще, но вы поймете идею.

#include <sstream>
#include <string>
#include <vector>
#include <map>

using namespace std;

vector<string> Tokenize(const string &str, const string &delim)
{
    vector<string> tokens;

    size_t p0 = 0, p1 = string::npos;
    while(p0 != string::npos)
    {
        p1 = str.find_first_of(delim, p0);
        if(p1 != p0)
        {
            string token = str.substr(p0, p1 - p0);
            tokens.push_back(token);
        }
        p0 = str.find_first_not_of(delim, p1);
    }

    return tokens;
}

int main()
{
    string data = "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";
    vector<string> entries = Tokenize(data, " ");
    map<string, int> items;

    for (size_t i = 0; i < entries.size(); ++i)
    {
        string item = entries[i];

        size_t pos = item.find_first_of('=');
        if(pos == string::npos)
            continue;

        string key = item.substr(0, pos);
        int value;
        stringstream stream(item.substr(pos + 1));
        stream >> value;
        items.insert (pair<string, int>(key, value));
    }

}
1 голос
/ 13 ноября 2009

То, чему вас учат здесь, это чудовища .

http://en.wikipedia.org/wiki/Scanf

Не используйте эту функцию для извлечения строк из ненадежных данных, но если вы либо доверяете данным, либо получаете только цифры, почему бы и нет.

Если вы знакомы с регулярными выражениями по использованию другого языка, используйте std::tr1::regex или boost::regex - они одинаковы. Если вы не знакомы, вы сделаете себе одолжение, ознакомившись.

1 голос
/ 07 ноября 2008

К сожалению, ваш исходный файл данных ориентирован на человека, а это значит, что вам придется выполнить кучу разборов строк, чтобы получить его в структуре. В противном случае, если данные были записаны непосредственно в виде двоичного файла, вы можете просто использовать fread (), чтобы вставить их непосредственно в структуру.

Если вы хотите использовать «элегантный» (то есть уродливый минималистический подход), вы можете сделать своего рода цикл для разбора каждой строки, в основном используя strchr (), чтобы сначала найти символ «=», затем следующий пробел затем с помощью atoi () преобразовать каждое число в реальное целое, а затем с помощью некоторого хакерского указателя перенести их все в структуру. Очевидный недостаток заключается в том, что если структура изменится или даже каким-либо образом реорганизуется, то весь алгоритм здесь молча сломается.

Таким образом, для чего-то, что было бы более удобным и читабельным (но приводило бы к большему количеству кода), вы можете просто вставить каждое значение в вектор, а затем пройти через вектор и скопировать каждое значение в соответствующее поле Strucutre. *

...