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

BEncode 在 C++ 中的词法分析 - 第二阶段

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2009 年 6 月 22 日

CPOL

1分钟阅读

viewsIcon

19934

downloadIcon

153

在前一个词法分析器基础上更进一步。

引言

在上一篇文章(bencodelex.aspx)中,我展示了一个相对简单的 BEncode 流词法分析器。它所能做的只是扫描传入的流,并向客户端代码通知正在解析的每个词法单元。 在本文中,我将其提升到一个新的水平,并展示一个 BEncode 解析器。 区别在于,现在它可以将整个数据结构组合在一起,供客户端浏览、迭代、转储,等等…

使用代码

这个解析器与我上一篇文章中展示的词法分析器之间的另一个重要区别在于客户端代码使用它的方式。 向解析器抛出任意数据块的方法没有改变。 但是,不再有回调函数。 你只需向解析器输入数据,直到数据完全被处理完毕,或者发生错误。

完成之后,解析器实例将为你提供对根节点的访问权限,你可以在那里开始你的搜索。

BENCODE 节点的样子

bencode_parser 类的内部数据结构基于 bencode_variant 结构,其结构如下

typedef struct _bencode_variant
{
 bntype vt;
 union
 {
  longlong i64;
  bnstring str;
  struct
  {
   struct _bencode_variant* first;
   struct _bencode_variant* last;
  } lst;
 } v;
 bnstring key;
 struct _bencode_variant* next;
} bencode_variant;

它实际上是一个带额外信息的类型化联合体,允许它链接到其他节点并与名称关联。 bntypebnstrings 在头文件中定义。

以下代码是我用来测试该类的一个示例程序中的片段,同时也演示了该类的典型用法

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

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

  fclose(pf);
}

上面的代码调用了一个名为 dump() 的函数,该函数以简单的 XML 格式递归地转储解析器收集的数据。 它的样子可能是这样的

void dump(const bencode::bencode_variant* pvar)
{
 int i;
 switch( pvar->vt )
 {
  case bencode::bntInteger:
  {
   if( pvar->key.len )
    fprintf(stdout, "<integer name=\"%s\">%lu</integer>\n", 
            pvar->key.pch, pvar->v.i64);
   else
    fprintf(stdout, "<integer>%lu</integer>\n", pvar->v.i64);
   break;
  }
  case bencode::bntString:
  {
   if( pvar->key.len )
    fprintf(stdout, "<string length=\"%lu\" name=\"%s\">", 
            pvar->v.str.len, pvar->key.pch);
   else
    fprintf(stdout, "<string length=\"%lu\">", pvar->v.str.len);
   dump_string(pvar->v.str.pch, pvar->v.str.len);
   fprintf(stdout, "</string>\n");
   break;
  }
  case bencode::bntList:
  {
   if( pvar->key.len )
    fprintf(stdout, "<list name=\"%s\">\n", pvar->key.pch);
   else
    fprintf(stdout, "<list>\n");
   for(const bencode::bencode_variant* p = pvar->v.lst.first; p; p = p->next)
    dump(p);
   fprintf(stdout, "</list>\n");
   break;
  }
  case bencode::bntDictionary:
  {
   if( pvar->key.len )
    fprintf(stdout, "<dictionary name=\"%s\">\n", pvar->key.pch);
   else
    fprintf(stdout, "<dictionary>\n");
   for(const bencode::bencode_variant* p = pvar->v.lst.first; p; p = p->next)
    dump(p);
   fprintf(stdout, "</dictionary>\n");
   break;
  }
 }
}

关注点

不知何故,我觉得下一步可以将收集到的数据转换为 DOM 结构,以便可以使用 XPath 进行搜索……还有什么比这更优雅的呢…

© . All rights reserved.