首页 > 代码库 > Ben编码解析的C++实现

Ben编码解析的C++实现

Ben编码的基本规则

B编码中有4种类型:字符串、整型、列表、字典。

字符串

字符串的编码格式为:<字符串的长度>:<字符串>,其中<>括号中的内容为必需。例如,有一个字符串spam,则经过B编码后为4:spam。

整型

整型的编码格式为:i<十进制的整型数>e,即B编码中的整数以i作为起始符,以e作为终结符,i为integer的第一个字母,e为end的第一个字母。例如,整数3,经过B编码后为i3e,整数?3的B编码为i?3e,整数0的B编码为i0e。

注意i03e不是合法的B编码,因为03不是十进制整数,而是八进制整数。

列表

列表的编码格式为:l<任何合法的类型>e,列表以l为起始符,以e为终结符,中间可以为任何合法的经过B编码的类型,l为list的第一个字母。例如,列表l4:spam4:eggse表示两个字符串,一个是spam,一个是eggs。

字典

字典的编码格式为:d<关键字><值>e,字典以d为起始符,以e为终结符,关键字是一个经过B编码的字符串,值可以是任何合法的B编码类型,在d和e之间可以出现多个关键字和值对,d是dictionary的第一个字母。例如,d4:spaml3:aaa3:bbbee,它是一个字典,该字典的关键字是spam,值是一个列表(以l开始,以e结束),列表中有两个字符串aaa和bbb。

又如:d9:publisher3:bob17:publisher-webpage15:www.example.come,它也是一个字典,第一个关键字是publisher,对应的值为bob,第二个关键字是publisher-webpage,对应的值是www.example.com。

对Ben编码的四种基本类型的封装

? 定义Ben编码类型的基类

四种基本类型(字符串、整型、列表、字典)均从此基类派生

class __declspec(dllexport) BCODE_TYPE_BASE: public std::enable_shared_from_this<BCODE_TYPE_BASE>{public:virtual int type() = 0;virtual void add_child(std::shared_ptr<BCODE_TYPE_BASE> child){}virtual void add_child(BCODE_TYPE_MAP_PAIR_ child) {}};

? 字符串类型定义

class __declspec(dllexport) BCODE_TYPE_STRING: public BCODE_TYPE_BASE{public:virtual int type() { return BCODE_STRING;}void append(char* pStr){strBuf_.append(pStr);}std::string& to_string() { return strBuf_;}protected:std::string strBuf_;};

? 整型类型定义

class __declspec(dllexport) BCODE_TYPE_INTEGER: public BCODE_TYPE_BASE{public:virtual int type() { return BCODE_INTEGER;}LONGLONG& value() { return llNumer_;}void value(LONGLONG llNum) { llNumer_ = llNum;}protected:LONGLONG llNumer_;};

? 列表类型定义

typedef std::vector<std::shared_ptr<BCODE_TYPE_BASE>> BCODE_TYPE_LIST_;class __declspec(dllexport) BCODE_TYPE_LIST : public BCODE_TYPE_BASE{public:virtual int type() { return BCODE_LIST;}void add_child(std::shared_ptr<BCODE_TYPE_BASE> child){push_back(child);}inline std::shared_ptr<BCODE_TYPE_BASE> at(size_t index) {if (index < 0 || index > list_.size()) return NULL;return list_[index];}inline void remove(size_t index){if (index < 0 || index > list_.size()) return;list_.erase(list_.begin() + index);}inline void push_back(std::shared_ptr<BCODE_TYPE_BASE> data) { list_.push_back(data);}inline std::vector<std::shared_ptr<BCODE_TYPE_BASE>>& list(){ return list_;}protected:BCODE_TYPE_LIST_ list_;};

? 字典类型定义

_ptr<BCODE_Ttypedef std::map<std::shared_ptr<BCODE_TYPE_STRING>,std::shared_ptr<BCODE_TYPE_BASE>> BCODE_TYPE_MAP_; class __declspec(dllexport) BCODE_TYPE_DICTIONARY : public BCODE_TYPE_BASE { public: virtual int type() { return BCODE_DICTIONARY;} void add_child(BCODE_TYPE_MAP_PAIR_ child) { insert(child.first,child.second); } inline void insert(std::shared_ptr<BCODE_TYPE_STRING> key, std::shared_ptr<BCODE_TYPE_BASE> value) { map_.insert(BCODE_TYPE_MAP_PAIR_(key,value)); } BCODE_TYPE_MAP_PAIR_ find(std::sharedYPE_STRING> key){BCODE_TYPE_MAP_::iterator iter = map_.find(key);if (iter == map_.end())return BCODE_TYPE_MAP_PAIR_(NULL,NULL);return BCODE_TYPE_MAP_PAIR_(iter->first,iter->second);}protected:BCODE_TYPE_MAP_ map_;};

解析Bencode编码文件

我们从*.torrent文件中读取Ben编码的数据内容。参照Ben编码规则,我们将数据分类型读取保存到列表中即可。核心算法如下:

int BenCoder::parser( FILE* fp,std::shared_ptr<BCODE_TYPE_BASE> parent){if (fp == NULL) return -1;char ch,szBuf[1024] = {0};// 分析BenCode编码文件while(1){// 默认每次读取1个字符if (getChar(fp,&ch) == -1)break;// 分类处理字符串、整型、列表、字典// 以数字开头则为字符串类型if (is_digit(ch)){memset(szBuf,0,1024);*szBuf = ch;// 读取字符串的长度信息,读取到‘:’停止if (read_until(fp,:,szBuf + 1,1024) == -1)return -1;LONGLONG llNumber = _atoi64(szBuf);LTM::DbgPrint("Type[String] Length[%s]",szBuf);// 读取字符串内容memset(szBuf,0,1024);std::shared_ptr<BCODE_TYPE_STRING> bString(new BCODE_TYPE_STRING);// 若读取的字符串内容超过1024则分段读取,反之则一次读取成功while (llNumber > 0){if (llNumber < 1024){if (getChars(fp,szBuf,llNumber,1024) == -1){return -1;}llNumber = 0;bString->append(szBuf);LTM::DbgPrint(" Content[%s]\n",szBuf);}else{if (getChars(fp,szBuf,1024,1024) == -1){return -1;}llNumber = llNumber - 1024;bString->append(szBuf);LTM::DbgPrint("->[%s]\n",szBuf);}}if (parent){// 若父结点为字典类型,则读取字符串类型时if (parent->type() == BCODE_DICTIONARY){// 若KEY为空,则表明之前未读取字符串做为关键值,此次读取的字符串应该为KEYif (key_ == NULL)key_ = bString;// KEY不为空,则表明之前已经读取字符串做为关键值,此次读取的字符串应该为VALUEelse{parent->add_child(BCODE_TYPE_MAP_PAIR_(key_,bString));key_ = NULL;}}else if (parent->type() == BCODE_LIST)parent->add_child(bString);elsedata_list_.push_back(bString);}elsedata_list_.push_back(bString);}else if (is_letter(ch)){// 整型if (ch == i || ch == I){memset(szBuf,0,1024);if (read_until(fp,e,szBuf,1024) == -1)return -1;LONGLONG llNumber = _atoi64(szBuf);std::shared_ptr<BCODE_TYPE_INTEGER> bInteger(new BCODE_TYPE_INTEGER);bInteger->value(llNumber);if (parent){if (parent->type() == BCODE_DICTIONARY){parent->add_child(BCODE_TYPE_MAP_PAIR_(key_,bInteger));key_ = NULL;}else if (parent->type() == BCODE_LIST)parent->add_child(bInteger);elsedata_list_.push_back(bInteger);}elsedata_list_.push_back(bInteger);LTM::DbgPrint("Type[Integer] Value[%s]\n",szBuf);}// 列表else if (ch == l || ch == L){LTM::DbgPrint("Type[List]\n");std::shared_ptr<BCODE_TYPE_LIST> bList(new BCODE_TYPE_LIST);if (parent){if (parent->type() == BCODE_DICTIONARY){parent->add_child(BCODE_TYPE_MAP_PAIR_(key_,bList));key_ = NULL;}else if (parent->type() == BCODE_LIST)parent->add_child(bList);elsedata_list_.push_back(bList);}elsedata_list_.push_back(bList);parser(fp,bList);}// 字典else if (ch == d || ch == D){LTM::DbgPrint("Type[Dictionary]\n");std::shared_ptr<BCODE_TYPE_DICTIONARY> bMap(new BCODE_TYPE_DICTIONARY);if (parent){if (parent->type() == BCODE_DICTIONARY){parent->add_child(BCODE_TYPE_MAP_PAIR_(key_,bMap));key_ = NULL;}else if (parent->type() == BCODE_LIST)parent->add_child(bMap);elsedata_list_.push_back(bMap);}elsedata_list_.push_back(bMap);parser(fp,bMap);}else if (ch == e){LTM::DbgPrint("end when read of ‘e‘\n");break;}// 未知else{LTM::DbgPrint("It has a unknow type when parser BCode File!\n");}}else{LTM::DbgPrint("It has a unknow error when parser BCode File!\n");}}return 0;}

源代码下载

最后附上完整代码的下载地址:http://download.csdn.net/detail/ltm5180/8001439

工程为DLL工程,对Ben编码文件的读取操作全部封装到BenCoder类中,使用示例:

BenCoder* coder = BenCoder::getInstance();

if (coder)

{

coder->SetstrFilepath("E:\\WorkSpaces\\proj\\BTLoader\\trunk\\Win32\\Debug\\ 0035.torrent");

coder->load();

}

BenCoder::freeInstance(coder);

Ben编码解析的C++实现