我有几个阵列:
const string a_strs[] = {"cr=1", "ag=2", "gnd=U", "prl=12", "av=123", "sz=345", "rc=6", "pc=12345"};
const string b_strs[] = {"cr=2", "sz=345", "ag=10", "gnd=M", "prl=11", "rc=6", "cp=34", "cv=54", "av=654", "ct=77", "pc=12345"};
然后我需要解析'='然后将值放在struct中。 (rc键映射到结构中的fc键),其格式为:
struct predict_cache_key {
pck() :
av_id(0),
sz_id(0),
cr_id(0),
cp_id(0),
cv_id(0),
ct_id(0),
fc(0),
gnd(0),
ag(0),
pc(0),
prl_id(0)
{ }
int av_id;
int sz_id;
int cr_id;
int cp_id;
int cv_id;
int ct_id;
int fc;
char gnd;
int ag;
int pc;
long prl_id;
};
我遇到的问题是数组不是与struct字段顺序或顺序相同。因此,我需要检查每个,然后提出一个方案,将相同的结构放入结构中。
使用C或C ++解决上述问题有何帮助?
答案 0 :(得分:6)
可能我没有正确理解,但显而易见的解决办法是将每个数组元素拆分为key
和value
,然后编写lo-o-ong if-else-if-else ...
序列,如
if (!strcmp(key, "cr"))
my_struct.cr = value;
else if (!strcmp(key, "ag"))
my_struct.ag = value;
...
您可以在C预处理器的帮助下自动创建此类序列,例如
#define PROC_KEY_VALUE_PAIR(A) else if (!strcmp(key,#A)) my_struct.##A = value
由于领先else
,您可以这样编写代码:
if (0);
PROC_KEY_VALUE_PAIR(cr);
PROC_KEY_VALUE_PAIR(ag);
...
你们中的一些结构字段的唯一问题是_id
后缀 - 对于它们你需要创建一个不同的宏来粘贴_id
后缀
答案 1 :(得分:3)
这不应该太难。你的第一个问题是你没有固定大小的数组,所以你必须传递数组的大小,或者我更喜欢你使数组以NULL结尾,例如。
const string a_strs[] = {"cr=1", "ag=2", "gnd=U", NULL};
然后我会编写一个解析字符串的(私有)辅助函数:
bool
parse_string(const string &str, char *buffer, size_t b_size, int *num)
{
char *ptr;
strncpy(buffer, str.c_str(), b_size);
buffer[b_size - 1] = 0;
/* find the '=' */
ptr = strchr(buffer, '=');
if (!ptr) return false;
*ptr = '\0';
ptr++;
*num = atoi(ptr);
return true;
}
然后你就可以做qrdl建议的了。
在一个简单的for循环中:
for (const string *cur_str = array; *cur_str; cur_str++)
{
char key[128];
int value = 0;
if (!parse_string(*cur_string, key, sizeof(key), &value)
continue;
/* and here what qrdl suggested */
if (!strcmp(key, "cr")) cr_id = value;
else if ...
}
编辑:你应该使用long而不是int和atol而不是atoi,因为你的prl_id属于long类型。第二,如果在'='之后可能有错误的格式化数字,你应该使用strtol,它可以捕获错误。
答案 2 :(得分:3)
我编写了一些小代码,允许您初始化字段,而不必过多担心字段是否因初始化而出现故障。
以下是您在自己的代码中使用它的方法:
/* clients using the above classes derive from lookable_fields */
struct predict_cache_key : private lookable_fields<predict_cache_key> {
predict_cache_key(std::vector<std::string> const& vec) {
for(std::vector<std::string>::const_iterator it = vec.begin();
it != vec.end(); ++it) {
std::size_t i = it->find('=');
set_member(it->substr(0, i), it->substr(i + 1));
}
}
long get_prl() const {
return prl_id;
}
private:
/* ... and define the members that can be looked up. i've only
* implemented int, char and long for this answer. */
BEGIN_FIELDS(predict_cache_key)
FIELD(av_id);
FIELD(sz_id);
FIELD(gnd);
FIELD(prl_id);
END_FIELDS()
int av_id;
int sz_id;
char gnd;
long prl_id;
/* ... */
};
int main() {
std::string const a[] = { "av_id=10", "sz_id=10", "gnd=c",
"prl_id=1192" };
predict_cache_key haha(std::vector<std::string>(a, a + 4));
}
框架如下
template<typename T>
struct entry {
enum type { tchar, tint, tlong } type_name;
/* default ctor, so we can std::map it */
entry() { }
template<typename R>
entry(R (T::*ptr)) {
set_ptr(ptr);
}
void set_ptr(char (T::*ptr)) {
type_name = tchar;
charp = ptr;
};
void set_ptr(int (T::*ptr)) {
type_name = tint;
intp = ptr;
};
void set_ptr(long (T::*ptr)) {
type_name = tlong;
longp = ptr;
};
union {
char (T::*charp);
int (T::*intp);
long (T::*longp);
};
};
#define BEGIN_FIELDS(CLASS) \
friend struct lookable_fields<CLASS>; \
private: \
static void init_fields_() { \
typedef CLASS parent_class;
#define FIELD(X) \
lookable_fields<parent_class>::entry_map[#X].set_ptr(&parent_class::X)
#define END_FIELDS() \
}
template<typename Derived>
struct lookable_fields {
protected:
lookable_fields() {
(void) &initializer; /* instantiate the object */
}
void set_member(std::string const& member, std::string const& value) {
typename entry_map_t::iterator it = entry_map.find(member);
if(it == entry_map.end()) {
std::ostringstream os;
os << "member '" << member << "' not found";
throw std::invalid_argument(os.str());
}
Derived * derived = static_cast<Derived*>(this);
std::istringstream ss(value);
switch(it->second.type_name) {
case entry_t::tchar: {
/* convert to char */
ss >> (derived->*it->second.charp);
break;
}
case entry_t::tint: {
/* convert to int */
ss >> (derived->*it->second.intp);
break;
}
case entry_t::tlong: {
/* convert to long */
ss >> (derived->*it->second.longp);
break;
}
}
}
typedef entry<Derived> entry_t;
typedef std::map<std::string, entry_t> entry_map_t;
static entry_map_t entry_map;
private:
struct init_helper {
init_helper() {
Derived::init_fields_();
}
};
/* will call the derived class's static init function */
static init_helper initializer;
};
template<typename T>
std::map< std::string, entry<T> > lookable_fields<T>::entry_map;
template<typename T>
typename lookable_fields<T>::init_helper lookable_fields<T>::initializer;
它使用鲜为人知的数据成员指针,您可以使用语法&classname::member
从类中获取。
答案 3 :(得分:1)
实际上,正如许多人所回答的那样,需要将解析问题与对象构造问题分开。工厂模式很适合。
Boost.Spirit库还以非常优雅的方式解决了parse-&gt;函数问题(使用EBNF表示法)。
我总是喜欢将“业务逻辑”与框架代码分开。
你可以通过以非常方便的方式开始写“你想做什么”来实现这一点,并从那里开始“你怎么做”。
const CMemberSetter<predict_cache_key>* setters[] =
#define SETTER( tag, type, member ) new TSetter<predict_cache_key,type>( #tag, &predict_cache_key::##member )
{ SETTER( "av", int, av_id )
, SETTER( "sz", int, sz_id )
, SETTER( "cr", int, cr_id )
, SETTER( "cp", int, cp_id )
, SETTER( "cv", int, cv_id )
, SETTER( "ct", int, ct_id )
, SETTER( "fc", int, fc )
, SETTER( "gnd", char, gnd )
, SETTER( "ag", int, ag )
, SETTER( "pc", int, pc )
, SETTER( "prl", long, prl_id )
};
PCKFactory<predict_cache_key> factory ( setters );
predict_cache_key a = factory.factor( a_strs );
predict_cache_key b = factory.factor( b_strs );
实现这一目标的框架:
// conversion from key=value pair to "set the value of a member"
// this class merely recognises a key and extracts the value part of the key=value string
//
template< typename BaseClass >
struct CMemberSetter {
const std::string key;
CMemberSetter( const string& aKey ): key( aKey ){}
bool try_set_value( BaseClass& p, const string& key_value ) const {
if( key_value.find( key ) == 0 ) {
size_t value_pos = key_value.find( "=" ) + 1;
action( p, key_value.substr( value_pos ) );
return true;
}
else return false;
}
virtual void action( BaseClass& p, const string& value ) const = 0;
};
// implementation of the action method
//
template< typename BaseClass, typename T >
struct TSetter : public CMemberSetter<BaseClass> {
typedef T BaseClass::*TMember;
TMember member;
TSetter( const string& aKey, const TMember t ): CMemberSetter( aKey ), member(t){}
virtual void action( BaseClass& p, const std::string& valuestring ) const {
// get value
T value ();
stringstream ( valuestring ) >> value;
(p.*member) = value;
}
};
template< typename BaseClass >
struct PCKFactory {
std::vector<const CMemberSetter<BaseClass>*> aSetters;
template< size_t N >
PCKFactory( const CMemberSetter<BaseClass>* (&setters)[N] )
: aSetters( setters, setters+N ) {}
template< size_t N >
BaseClass factor( const string (&key_value_pairs) [N] ) const {
BaseClass pck;
// process each key=value pair
for( const string* pair = key_value_pairs; pair != key_value_pairs + _countof( key_value_pairs); ++pair )
{
std::vector<const CMemberSetter<BaseClass>*>::const_iterator itSetter = aSetters.begin();
while( itSetter != aSetters.end() ) { // optimalization possible
if( (*itSetter)->try_set_value( pck, *pair ) )
break;
++itSetter;
}
}
return pck;
}
};
答案 4 :(得分:0)
问题是你没有在运行时引用struct元素的元信息(类似于structVar。$ ElementName = ...,其中$ ElementName不是元素名称而是包含元素的(char?)变量应该使用的名称)。 我的解决方案是添加这个元信息。 这应该是一个数组,其中包含结构中元素的偏移量。
Quick-n-Dirty解决方案:您添加一个包含字符串的数组,结果代码应如下所示:
const char * wordlist[] = {"pc","gnd","ag","prl_id","fc"};
const int offsets[] = { offsetof(mystruct, pc), offsetof(mystruct, gnd), offsetof(mystruct, ag), offsetof(mystruct, prl_id), offsetof(mystruct, fc)};
const int sizes[] = { sizeof(mystruct.pc), sizeof(mystruct.gnd), sizeof(mystruct.ag), sizeof(mystruct.prl_id), sizeof(mystruct.fc)}
输入你想要的东西:
index = 0;
while (strcmp(wordlist[index], key) && index < 5)
index++;
if (index <5)
memcpy(&mystructvar + offsets[index], &value, sizes[index]);
else
fprintf(stderr, "Key not valid\n");
如果你有更大的结构,插入的这个循环可能会花费很多,但C doenst允许使用字符串进行数组索引。但是计算机科学找到了解决这个问题的方法:完美的哈希。
所以事后看起来会像这样:
hash=calc_perf_hash(key);
memcpy(&mystruct + offsets[hash], &value, sizes[hash]);
但是如何获得这些完美的哈希函数(我称之为calc_perf_hash)? 有一些算法可以让你只填充你的关键字,并且功能出来了,幸运的是有人甚至对它们进行了编程:在你最喜欢的OS /发行版中寻找“gperf”工具/包。 在那里你只需要输入6个元素名称,然后输出你准备使用C代码的完美哈希函数(在默认情况下生成一个返回哈希值的函数“hash”)和一个“in_word_set”函数来决定是否给定键在单词列表中)。 因为哈希的顺序不同,所以你当然要按哈希的顺序初始化offsetof和size数组。
您遇到的另一个问题(以及其他答案未考虑的问题)是类型转换。其他人做了一个任务,我有(不是更好)记忆。 在这里,我建议您将sizes数组更改为另一个数组:
const char * modifier[]={"%i","%c", ...
每个字符串都描述了sscanf修饰符来读取它。这样你可以用
替换赋值/副本sscanf(valueString, modifier[hash], &mystructVar + offsets(hash));
Cf当然,你可以在这里改变,通过将“element =”包含在字符串或类似字符串中。所以你可以将完整的字符串放入值中,而不必预处理它,我认为这很大程度上取决于你们其余的解析例程。
答案 5 :(得分:0)
如果我在直接C中这样做,我不会使用所有的母亲。相反,我会做这样的事情:
typedef struct {
const char *fieldName;
int structOffset;
int fieldSize;
} t_fieldDef;
typedef struct {
int fieldCount;
t_fieldDef *defs;
} t_structLayout;
t_memberDef *GetFieldDefByName(const char *name, t_structLayout *layout)
{
t_fieldDef *defs = layout->defs;
int count = layout->fieldCount;
for (int i=0; i < count; i++) {
if (strcmp(name, defs->fieldName) == 0)
return defs;
defs++;
}
return NULL;
}
/* meta-circular usage */
static t_fieldDef metaFieldDefs[] = {
{ "fieldName", offsetof(t_fieldDef, fieldName), sizeof(const char *) },
{ "structOffset", offsetof(t_fieldDef, structOffset), sizeof(int) },
{ "fieldSize", offsetof(t_fieldDef, fieldSize), sizeof(int) }
};
static t_structLayout metaFieldDefLayout =
{ sizeof(metaFieldDefs) / sizeof(t_fieldDef), metaFieldDefs };
这使您可以在运行时使用结构布局的紧凑集合按名称查找字段。这很容易维护,但我不喜欢实际使用代码中的sizeof(mumble)
- 这要求所有结构定义都标有注释,“不要改变类型或内容而不更改它们在此结构的t_fieldDef
数组中“。还需要进行NULL
检查。
我也更喜欢查找是二进制搜索还是哈希,但对于大多数情况来说这可能已经足够了。如果我要做哈希,我会将NULL
哈希表的指针放入t_structLayout
并在第一次搜索时构建哈希值。
答案 6 :(得分:0)
尝试了你的想法并得到了一个
error: ISO C++ forbids declaration of ‘map’ with no type
在linux ubuntu eclipse cdt。
我希望通知我应该在“* .h”文件中包含<map>
为了在没有此错误消息的情况下使用您的代码。
#include <map>
// a framework
template<typename T>
struct entry {
enum type { tchar, tint, tlong } type_name;
/* default ctor, so we can std::map it */
entry() { }
template<typename R>
entry(R (T::*ptr)) {
等'等'......