• 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() = 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_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 -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]
    ",szBuf);
    
    }
    
    else
    
    {
    
    if (getChars(fp,szBuf,1024,1024) == -1)
    
    {
    
    return -1;
    
    }
    
    llNumber = llNumber - 1024;
    
    bString->append(szBuf);
    
    LTM::DbgPrint("->[%s]
    ",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,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);
    
    else
    
    data_list_.push_back(bInteger);
    
    }
    
    else
    
    data_list_.push_back(bInteger);
    
    LTM::DbgPrint("Type[Integer] Value[%s]
    ",szBuf);
    
    }
    
    // 列表
    
    else if (ch == 'l' || ch == 'L')
    
    {
    
    LTM::DbgPrint("Type[List]
    ");
    
    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]
    ");
    
    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'
    ");
    
    break;
    
    }
    
    // 未知
    
    else
    
    {
    
    LTM::DbgPrint("It has a unknow type when parser BCode File!
    ");
    
    }
    
    }
    
    else
    
    {
    
    LTM::DbgPrint("It has a unknow error when parser BCode File!
    ");
    
    }
    
    }
    
    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);

  • 相关阅读:
    codeforces 1215 E Marbles-----状压DP
    留坑待填
    Catalan数
    砝码称重
    约数和
    硬币题解
    迎春舞会之数字舞蹈
    过剩数
    猜测棋局
    [NOIP普及组2014第三题]螺旋矩阵
  • 原文地址:https://www.cnblogs.com/ltm5180/p/4005940.html
Copyright © 2020-2023  润新知