【问题标题】:XML CharacterDataHandler callback unpextedly called multiple timesXML CharacterDataHandler 回调被多次调用
【发布时间】:2017-02-08 23:49:01
【问题描述】:

我正在学习 libexpat。我将这个示例拼凑在一起,以便基本熟悉使用 API:

守则

#include <stdio.h>
#include <expat.h>
#include <string.h>
#include <iostream>

void start(void* userData, const char* name, const char* argv[])
{
  std::cout << "name: " << name << std::endl;

  int i = 0;

  while (argv[i])
  {
    std::cout << "argv[" << i << "] == " << argv[i++] << std::endl;
  }
}

void end(void* userData, const char* name)
{
}

void value(void* userData, const char* val, int len)
{
  char str[len+1];
  strncpy(str, val, len);
  str[len] = '\0';

  std::cout << "value: " << str << std::endl;
}

int main(int argc, char* argv[], char* envz[])
{
  XML_Parser parser = XML_ParserCreate(NULL);
  XML_SetElementHandler(parser, start, end);
  XML_SetCharacterDataHandler(parser, value);

  int bytesRead = 0;
  char val[1024] = {};
  FILE* fp = fopen("./catalog.xml", "r");
  std::cout << "fp == 0x" << (void*)fp << std::endl;

  do
  {
    bytesRead = fread(val, 1, sizeof(val), fp);
    std::cout << "In while loop bytesRead==" << bytesRead << std::endl;

    if (0 == XML_Parse(parser, val, bytesRead, (bytesRead < sizeof(val))))
    {
      break;
    }
  }
  while (1);

  XML_ParserFree(parser);
  std::cout << __FUNCTION__ << " end" << std::endl;

  return 0;
}

catalog.xml

<CATALOG>
    <CD key1="value1" key2="value2">
        <TITLE>Empire Burlesque</TITLE>
        <ARTIST>Bob Dylan</ARTIST>
        <YEAR>1995</YEAR>
    </CD>
</CATALOG>

生成文件

xml: xml.o
        g++ xml.o -lexpat -o xml

xml.o: main.cpp Makefile
        g++ -g -c main.cpp -o xml.o

输出

fp == 0x0x22beb50
In while loop bytesRead==148
name: CATALOG
value: 

value:     
name: CD
argv[1] == key1
argv[2] == value1
argv[3] == key2
argv[4] == value2
value: 

value: 
name: TITLE
value: Empire Burlesque
value: 

value: 
name: ARTIST
value: Bob Dylan
value: 

value: 
name: YEAR
value: 1995
value: 

value:     
value: 

In while loop bytesRead==0
main end

问题

从输出中,我使用XML_SetCharacterDataHandler() 安装的回调似乎为 CATALOG、、CD、TITLE 和 ARTIST xml 标签调用了两次,然后为 YEAR 标签调用了多次 - 有人可以解释这种行为吗?从上面提到的catalog.xml 来看,我不清楚为什么会有(或永远会有)多个值与任何 XML 标记相关联。

谢谢。

引用

感谢this site 以上示例代码的基础。

【问题讨论】:

    标签: c xml expat-parser


    【解决方案1】:

    expat 解析器可能将文本节点拆分为对字符数据处理程序的多个调用。要正确处理文本节点,您必须在多次调用中累积文本并在接收到包含标签的“结束”事件时对其进行处理。

    这通常是正确的,即使在不同的解析器和不同的语言中也是如此——也就是说,在 Java 中也是如此。

    例如见http://marcomaggi.github.io/docs/expat.html#using-comm

    对于 XML 解析器的任何面向事件的接口,一个常见的第一次错误是期望包含在一个元素中的所有文本都可以通过对字符数据处理程序的一次调用来报告。与许多其他 XML 解析器一样,Expat 将此类数据报告为一系列调用;在进行不同的回调之前,无法知道何时到达序列的末尾。

    同样来自the expat documentation

    没有标记的单个连续文本块仍可能导致对该处理程序的一系列调用。换句话说,如果您在文本中搜索模式,它可能会被拆分为对该处理程序的调用。

    【讨论】:

    • 我是否理解正确:每次调用start 回调时,我都应该记下name 参数。例如。假设 start 回调是用name=="foo" 调用的,那么 foo 标签的值实际上是所有传入数据处理回调的字符串的累加,而当用 name=="foo" 调用 end 回调时可以终止这种累加。
    • 我认为我的理解有问题:标签“TITLE”的开始回调在“CATALOG”的结束回调之前被调用 - 所以我认为这意味着 TITLE 的值会累积到后续调用使用不同的名称开始回调...?
    • 标签 "TITLE" 的开始回调在 "CATALOG" 的结束回调之前被调用 -- 没错,&lt;CATALOG&gt; 元素的结束还没有发生.直到文档的最后,当遇到&lt;/CATALOG&gt; 时,它才会发生。为什么您希望在 start-&lt;TITLE&gt; 事件之前看到它?
    • 在“目录”的结束回调之前调用“标题”的开始回调是有道理的,但是我不明白如何将您的原始评论付诸实践:“目录标签的价值实际上是传入数据处理程序回调的所有字符串的累积,并且可以在使用 name=="CATALOG"" 调用结束回调时终止累积。但是,如果我们收到 TITLE 的开始回调但尚未收到 CATALOG 的结束回调,数据处理程序如何知道它传递的 val 参数属于 CATALOG 还是 TITLE?
    • 对不起,我引用的是我自己而不是你。您的引用我不明白如何付诸实践:“您必须在多次调用中累积文本并在接收到包含标签的“结束”事件时对其进行处理”。但同样的问题:如果我们收到 TITLE 的开始回调但尚未收到 CATALOG 的结束回调,数据处理程序如何知道它传递的 val 参数属于 CATALOG 还是 TITLE?
    猜你喜欢
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    • 2015-12-08
    • 2018-06-02
    • 2015-04-23
    • 1970-01-01
    • 2020-06-08
    • 2010-10-24
    相关资源
    最近更新 更多