Bencode编码解析的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() = ; 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 < || index > list_.size()) return NULL; return list_[index]; } inline void remove(size_t index) { if (index < || 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_T typedef 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::shared YPE_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 -; char ch,szBuf[] = {}; // 分析BenCode编码文件 while() { // 默认每次读取1个字符 if (getChar(fp,&ch) == -) break; // 分类处理字符串、整型、列表、字典 // 以数字开头则为字符串类型 if (is_digit(ch)) { memset(szBuf,,); *szBuf = ch; // 读取字符串的长度信息,读取到‘:’停止 if (read_until(fp,':',szBuf + ,) == -) return -; LONGLONG llNumber = _atoi64(szBuf); LTM::DbgPrint("Type[String] Length[%s]",szBuf); // 读取字符串内容 memset(szBuf,,); std::shared_ptr<BCODE_TYPE_STRING> bString(new BCODE_TYPE_STRING); // 若读取的字符串内容超过1024则分段读取,反之则一次读取成功 while (llNumber > ) { if (llNumber < ) { if (getChars(fp,szBuf,llNumber,) == -) { return -; } llNumber = ; bString->append(szBuf); LTM::DbgPrint(" Content[%s]\n",szBuf); } else { if (getChars(fp,szBuf,,) == -) { return -; } llNumber = llNumber - ; bString->append(szBuf); LTM::DbgPrint("->[%s]\n",szBuf); } } if (parent) { // 若父结点为字典类型,则读取字符串类型时 if (parent->type() == BCODE_DICTIONARY) { // 若KEY为空,则表明之前未读取字符串做为关键值,此次读取的字符串应该为KEY if (key_ == NULL) key_ = bString; // KEY不为空,则表明之前已经读取字符串做为关键值,此次读取的字符串应该为VALUE else { parent->add_child(BCODE_TYPE_MAP_PAIR_(key_,bString)); key_ = NULL; } } else if (parent->type() == BCODE_LIST) parent->add_child(bString); else data_list_.push_back(bString); } else data_list_.push_back(bString); } else if (is_letter(ch)) { // 整型 if (ch == 'i' || ch == 'I') { memset(szBuf,,); if (read_until(fp,'e',szBuf,) == -) return -; 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); else data_list_.push_back(bInteger); } else data_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); else data_list_.push_back(bList); } else data_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); else data_list_.push_back(bMap); } else data_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 ; }
源代码下载
最后附上完整代码的下载地址: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);
Bencode编码解析的C++实现的更多相关文章
- 小记 js unicode 编码解析
原文:小记 js unicode 编码解析 var str = "\\u6211\\u662Funicode\\u7F16\\u7801"; 关于这样的数据转换为中文问题,常用的两 ...
- Python解析xml文件遇到的编码解析的问题
使用python对xml文件进行解析的时候,假设xml文件的头文件是utf-8格式的编码,那么解析是ok的,但假设是其它格式将会出现例如以下异常: xml.parsers.expat.ExpatErr ...
- HTTP1.1中CHUNKED编码解析(转载)
HTTP1.1中CHUNKED编码解析 一般HTTP通信时,会使用Content-Length头信息性来通知用户代理(通常意义上是浏览器)服务器发送的文档内容长度,该头信息定义于HTTP1.0协议RF ...
- XCTF练习题---CRYPTO---混合编码解析
XCTF练习题---CRYPTO---混合编码解析 flag:cyberpeace{welcometoattackanddefenceworld} 解题步骤: 1.观察题目,下载附件进行查看 2.看到 ...
- 中国天气网 JSON接口的城市编码解析及结果
最近在弄一个Android应用,其中一个功能是天气情况展示,准备使用google的天气API服务(http://www.google.com/ig/api?hl=zh-cn&weather=, ...
- js小记 unicode 编码解析
var str = "\\u6211\\u662Funicode\\u7F16\\u7801"; // 关于这样的数据转换为中文问题,常用的两种方法. // 1. eval 解析 ...
- Spring第七弹—依赖注入之注解方式注入及编码解析@Resource原理
注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果. 手工装配依赖对象 手工装配依赖对象,在这种方式中又有两种编 ...
- HTTP1.1中CHUNKED编码解析
一般HTTP通信时,会使用Content-Length头信息性来通知用户代理(通常意义上是浏览器)服务器发送的文档内容长度,该头信息定义于HTTP1.0协议RFC 1945 10.4章节中.浏览器 ...
- Java Web中的编码解析
在springmvc工程web.xml中配置中文编码 <!-- 配置请求过滤器,编码格式设为UTF-8,避免中文乱码--> <filter> <filter-name&g ...
随机推荐
- 转:VS2010与SVN
在VS2010中使用SVN,必须先安装SVN的客户端,再安装VisualSVN(SVN的插件).必须保证两者的版本不冲突,我现在安装的是TortoiseSVN-1.7.10.23359-win32-s ...
- mongodb教程
http://sheperd.blog.163.com/blog/static/19525511920119111149046/ http://www.cnblogs.com/oec2003/arch ...
- org.quartz.utils.UpdateChecker Checking for available updated version of Quartz..
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</ ...
- "红色病毒"问题 HDU 2065 递推+找循环节
题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=2065 递推类题目, 可以考虑用数学方法来做, 但是明显也可以有递推思维来理解. 递推的话基本就是状态 ...
- [Java Concurrent] 并发访问共享资源的简单案例
EvenGenerator 是一个偶数生成器,每调用一个 next() 就会加 2 并返回叠加后结果.在本案例中,充当被共享的资源. EvenChecker 实现了 Runnable 接口,可以启动新 ...
- spring配置文件中id与name
1.id属性命名必须满足XML的命名规范,因为id其实是XML中就做了限定的. 2.name属性则没有这些限定,你可以使用几乎任何的名称 3.配置文件中不允许出现两个id相同的<bean> ...
- JSP学习笔记(二):Tomcat服务器的安装及配置
一.Tomcat的下载及安装. 前往Tomcat官网下载安装包或者免安装压缩包.链接http://tomcat.apache.org/ 这里,我选择的是Tomcat8.0,而不是最新的Tomcat9. ...
- OperationalError:(1054 - "Unknown column 'game.lable1' in 'field list' ")解决办法
今天白天遇到一个错误,第一次遇到这样的问题,数据库的问题,百度了很多答案也找了很多博客文章看 问题:OperationalError:(1054 - "Unknown column 'gam ...
- QT5 TK1 串口通信
对TK1中基于QT5的串口通信过程进行总结.按照软件安装及通信实现的顺序. 1.QT5安装 较简洁方式:打开软件中心(类似A形),搜索qtcreator,点击安装即可. 2.串口通信库安装 采用上述方 ...
- C++ —— 非常量引用不能指向临时对象
目录 举例 分析 解决 1.举例 非常量引用 指向 临时对象 —— 即:将 临时对象 传递给 非常量引用类型. 如以下情况就会出现: 实现实数Rational类,实数可以使用+号相加,运算的结果要可以 ...