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 ...
随机推荐
- 转:windows xp下如何安装SQL server2000企业版
SQL2000企业版本 适用于WIN 2000 Server系统和Windows 2003系统,Windows XP一般装不了需要选用个人版或开发板.但是企业版也可以安装在xp系统下.这里介绍一个XP ...
- spring-boot 测试
在线项目构建:http://start.spring.io/ 下载:bookpub.zip 导入Idea15.0.1 运行: y@y:bookpub$ ./gradlew clean bootRun ...
- 转载:关于 Token,你应该知道的十件事
关于 Token,你应该知道的十件事 原文地址:http://alvinzhu.me/blog/2014/08/26/10-things-you-should-know-about-tokens/ 原 ...
- SDUT2484 算术表达式的转换(表达式树)
题目链接. 分析: 转换成表达式树,然后先序.中序.后序遍历. AC代码如下: #include <stdio.h> #include <string.h> #define m ...
- 设计模式(二): BUILDER生成器模式 -- 创建型模式
1.定义 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式. 2.适用场景 1. 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式 ...
- tinyxml开源库的基本用法
最近项目中的某个功能需要写xml,由于项目中已经引入了tinyxml,所以不再寻找其他开源库. 前提:你得有个xml对象,声明tinyxml的对象:基于tinyxml的内存管理,TiXmlDocume ...
- 谷歌 google
google Google是搜索引擎名,也是一家美国上市公司名称.Google公司于1998年9月7日以私有股份公司的形式创立,以设计并管理一个互联网的搜索引擎.Google公司的总部称作“Googl ...
- J - Sabotage - UVA 10480(最大流)
题目大意:旧政府有一个很庞大的网络系统,可以很方便的指挥他的城市,起义军为了减少伤亡所以决定破坏他们的网络,使他们的首都(1号城市)和最大的城市(2号城市)不能联系,不过破坏不同的网络所花费的代价是不 ...
- Ubuntu 用户及组管理
$是普通管员,#是系统管理员,在Ubuntu下,root用户默认是没有密码的,因此也就无法使用(据说是为了安全).想用root的话,得给root用户设置一个密码:sudo passwd root然后登 ...
- JQuery的ready函数与JS的onload的区别详解
JQuery的ready函数与JS的onload的区别:1.执行时间window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行.$(document).ready()是DOM结构绘制 ...