65.9K
CodeProject 正在变化。 阅读更多。
Home

C++ 中的 BEncode 解析

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (3投票s)

2009年6月21日

CPOL

2分钟阅读

viewsIcon

24945

downloadIcon

221

一个非常高效的 C++ BEncode 词法分析器。

引言

BENCODE 是一种数据编码方法,与 .torrent 文件紧密相关。本文介绍了一个可以解码该格式流的 C++ 类。

背景

实际上,我在一个正在进行的项目中需要一个 BENCODE 词法分析代码,无论我如何广泛地进行网络搜索,我所能找到的要么是非 C/C++ 代码,要么代码过于依赖我无法找到的其他文件,或者代码处理了我不需要的事情。所以,像往常一样,对我来说唯一合理的解决方案是花几个小时自己完成,按照我想要的方式。

使用代码

在进入完全直接的实际用法部分之前,我想强调一下 bencode_lexer 类的一些要点

  • bencode_lexer 具有流感知能力,这意味着您可以按代码读取的顺序向其提供任意大小的数据块。
  • 该类还充当解析上下文。换句话说,一个 bencode_lexer 实例一次只能处理一个内容流。
  • 该类不是线程安全的,但由于它不应该在并行流上运行,我认为这完全可以接受。

这是一个简单的代码,它将 bencode 流的内容转储到标准输出设备。它可能在现实世界中不太有用,但它展示了如何使用该类

#include <stdio.h>
#include "bencode.h"

int g_iIdent = 0;

bool Callback(bencode_lexer::Event e, const void* pv, int cb, long lParam)
{
 switch( e )
 {
  case bencode_lexer::eBeginDictionary:
  {
   for(int i=0; i < g_iIdent; ++i) fputc('\t', stdout);
   fputs("dictionary {\n", stdout);
   ++g_iIdent;
   break;
  }
  case bencode_lexer::eEndDictionary:
  {
   --g_iIdent;
   for(int i=0; i < g_iIdent; ++i) fputc('\t', stdout);
   fputs("}\n", stdout);
   break;
  }
  case bencode_lexer::eBeginList:
  {
   for(int i=0; i < g_iIdent; ++i) fputc('\t', stdout);
   fputs("list {\n", stdout);
   ++g_iIdent;
   break;
  }
  case bencode_lexer::eEndList:
  {
   --g_iIdent;
   for(int i=0; i < g_iIdent; ++i) fputc('\t', stdout);
   fputs("}\n", stdout);
   break;
  }
  case bencode_lexer::eInteger:
  {
   for(int i=0; i < g_iIdent; ++i) fputc('\t', stdout);
   fprintf(stdout, "integer=%lu\n", *(int*)pv);
   break;
  }
  case bencode_lexer::eString:
  {
   for(int i=0; i < g_iIdent; ++i) fputc('\t', stdout);
   fprintf(stdout, "string=%s\n", (const char*)pv);
   break;
  }
 }
 return true;
}

int main(int argc, char* argv[])
{
 if( argc < 2 )
  return 1;

 FILE* pf = fopen(argv[1], "rb");
 if( pf )
 {
  bencode_lexer bed(Callback);
  char chBuf[1024];

  for(;;)
  {
   int cbRead = fread(chBuf, 1, sizeof(chBuf), pf);
   if( cbRead < 1 )
    break;
   int cbParsed = bed.lex_chunk(chBuf, cbRead, 0);
   if( cbParsed < cbRead )
    break;
  }

  fclose(pf);
 }

 return 0;
}

再次说明,由于 bencode_lexer 不创建任何数据结构,因此您需要根据项目的需要构建提取的数据。

关注点

bencode_lexer 使用手写的堆栈类来管理其内部状态机。该类以一种更有限但更有效的方式模拟了 STL 的堆栈类。在实际项目中,您需要替换 ASSERT 调用中我放置的硬编码断点。

© . All rights reserved.