使用C++分析文件,将值加载到结构中



我有以下文件/行:

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++的新手,不知道有任何函数或库可以完成这项工作。

每一行都要进行处理,在刷新之前,结构每次都会填充一行并使用它。该结构后来被用作函数的参数。

您可以这样做:

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。我希望这不是太多的信息。

这是为您的结构定义流运算符的最佳位置:

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

这似乎奏效了。当然,你会提取我写的主要代码,并将其粘贴在类或其他类中,但你会明白的。

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

不幸的是,您的源数据文件是面向人的,这意味着您必须进行一系列字符串解析才能将其放入结构中。否则,如果数据直接作为二进制文件写入,则可以使用fread()将其直接弹出到结构中。

如果你想使用"优雅"(即丑陋的极简主义方法),你可以做一个循环来解析每一行,基本上是使用strchr()首先找到"="字符,然后找到下一个空格,然后使用atoi()将每个数字转换为实整数,然后使用一些指针技巧将它们全部推入结构中。明显的缺点是,如果结构发生了变化,甚至以某种方式进行了重组,那么这里的整个算法就会悄无声息地崩溃。

因此,对于更易于维护和可读(但会产生更多代码)的东西,您可以将每个值推送到一个向量中,然后遍历该向量并将每个值复制到适当的strucutre字段中。

你在这里学到的是怪物

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

不要使用此函数从不受信任的数据中提取字符串,但只要您信任数据,或者只获取数字,为什么不呢。

如果您熟悉使用其他语言的正则表达式,请使用std::tr1::regexboost::regex—它们是相同的。如果不熟悉,你会通过熟悉自己来帮自己一个忙。

相关内容

  • 没有找到相关文章

最新更新