我有以下文件/行:
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::regex
或boost::regex
—它们是相同的。如果不熟悉,你会通过熟悉自己来帮自己一个忙。