1

我正在学习 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;
}

目录.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 标记相关联。

谢谢你。

引文

归功于本网站的上述示例代码的基础。

4

1 回答 1

2

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

这通常是正确的,即使跨不同的解析器和不同的语言——即同样的事情在 Java 中也是正确的。

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

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

也来自外籍人士文档

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

于 2017-02-08T23:56:03.627 回答