场景
-
在
Windows
,Linux
和macOS
上开发C/C++
版本XML
处理程序时, 我们通常会使用libxml2
库, 这个库可以跨平台, 而且在*ux
下都会自带这个库. 但是使用这个库时为什么可以不调用xmlInitParser()
初始化,是不是不需要调用? -
我们应该如何使用这个库进行读取大的
XML
文件? -
在使用某个
*Reader
的函数时怎么第二次调用就会崩溃?
图1:
说明
当前使用 libxml2
版本是 2.7.1
.
libxml2
支持以DOM
方式和SAX
方式读取XML
文件. 当我们以SAX
模式读取文件时,在Windows
上由于不支持UNICODE
路径,所以我们需要使用xmlReaderForIO
函数,因为这个函数可以传递FILE*
类型的指针,这样我们就可以通过Windows
提供的_wfopen
版本打开UNICODE
路径的文件. 这个函数需要我们自定义读取文件的回调函数xmlInputReadCallback
和xmlInputCloseCallback
,也就是代理fread
读取文件函数和fclose
关闭文件函数.
xmlTextReaderPtr XMLCALL
xmlReaderForIO (xmlInputReadCallback ioread,
xmlInputCloseCallback ioclose,
void *ioctx,
const char *URL,
const char *encoding,
int options);
-
这个函数
xmlReaderForIO
在头文件xmlreader.h
里. 它的读取性能非常高效,页不需要通过fseek
进行文件指针移动, 一直是顺序读取文件,唯一的麻烦就是要自己处理XML
的元素进入和离开的分析. 如果是复杂的树形结构,那么需要自己维护一个进入退出的标记。 我这里的项目例子只有两层。 -
通常情况下,我们使用
XML
文件保存文本类型的数据。
例子
- 如果我们调用
TestLibxml2(2);
开启两个线程时,没有调用xmlInitParser()
或者LIBXML_TEST_VERSION
初始化,就会崩溃.
图2:
info_reader.h
#pragma once
#include <string>
#include <vector>
#include <memory>
using namespace std;
class Info
{
public:
Info() :_id(0), type(0) {}
int64_t _id;
string name;
string location;
string date;
string duration;
string number;
int type;
string typeStr;
};
class InfoReader
{
public:
int ReadXml(vector<shared_ptr<Info>>* infos, const char* path);
};
info_reader.cpp
#include "info_reader.h"
#include "libxml/parser.h"
#include "libxml/tree.h"
#include "libxml/xmlreader.h"
static const char* kArrayElement = "ARRAY";
static const char* kInfoElement = "info";
static const char* kIdElement = "id";
static const char* kNameElement = "name";
static const char* kDurationElement = "duration";
static const char* kNumberElement = "number";
static const char* kLocationElement = "location";
static const char* kDateElement = "date";
static const char* kTypeElement = "type";
static const char* kTypeStrElement = "typeStr";
static const int kMaxSizeBuf = 256;
static int pXmlInputReadCallback(void * context, char * buffer, int len)
{
FILE* file = (FILE*)context;
int readed = fread(buffer, 1, len, file);
return readed;
}
static int pXmlInputCloseCallback(void * context)
{
FILE* file = (FILE*)context;
return fclose(file);
}
static int ProcessNode(xmlTextReaderPtr reader, Info* last,char last_element[]) {
const xmlChar *name, *value;
name = xmlTextReaderConstName(reader);
if (name == NULL)
name = BAD_CAST "--";
value = xmlTextReaderConstValue(reader);
int type = xmlTextReaderNodeType(reader);
int result = 0;
switch (type)
{
case XML_READER_TYPE_TEXT:
{
if (!last || !value)
break;
if (!strcmp((const char*)last_element, kIdElement)) {
last->_id = _atoi64((const char*)value);
}else if (!strcmp((const char*)last_element, kNameElement)) {
last->name = (const char*)value;
}else if (!strcmp((const char*)last_element, kDurationElement)) {
last->duration= (const char*)value;
}else if (!strcmp((const char*)last_element, kDateElement)) {
last->date = (const char*)value;
}else if (!strcmp((const char*)last_element, kNumberElement)) {
last->number = (const char*)value;
}else if (!strcmp((const char*)last_element, kLocationElement)) {
last->location = (const char*)value;
}else if (!strcmp((const char*)last_element, kTypeElement)) {
last->type = atoi((const char*)value);
}else if (!strcmp((const char*)last_element, kTypeStrElement)) {
last->typeStr = (const char*)value;
}
break;
}
case XML_READER_TYPE_ELEMENT:
{
strncpy(last_element, (const char*)name, kMaxSizeBuf);
if (!strcmp((const char*)name, kInfoElement)) {
result = 1;
}
break;
}
case XML_READER_TYPE_END_ELEMENT:
{
break;
}
}
return result;
}
int InfoReader::ReadXml(vector<shared_ptr<Info>>* infos, const char* path)
{
// 如果是 Windows 平台,需要unicode版本 _wfopen.这样才可以识别其他语言的路径.
FILE* file = fopen(path, "rb");
if (!file)
return 0;
xmlTextReaderPtr reader = xmlReaderForIO(pXmlInputReadCallback, pXmlInputCloseCallback, file,
NULL, "UTF-8", XML_PARSE_RECOVER);
if (!reader) {
fclose(file);
return 0;
}
Info* last = NULL;
char last_element[kMaxSizeBuf] = { 0 };
int index = 0;
while (xmlTextReaderRead(reader) == 1) {
int ret = ProcessNode(reader, last, last_element);
if (ret) {
last = new Info();
infos->push_back(shared_ptr<Info>(last));
++index;
}
}
xmlTextReaderClose(reader);
return index;
}
test-libxml2.cpp
// test-libxml2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <thread>
#include "info_reader.h"
#include "libxml/parser.h"
using namespace std;
template<typename T>
static void PRINT(const char* key, T value)
{
std::cout << key << " -> " << value << std::endl;
}
static void ReadXmlFunc()
{
vector<shared_ptr<Info>> infos;
InfoReader ir;
int size = ir.ReadXml(&infos, "..\\..\\test.xml");
PRINT("Read Size", size);
for (auto& info : infos) {
PRINT("=================================", "");
PRINT("name", info->name);
PRINT("location", info->location);
PRINT("number", info->number);
PRINT("date", info->date);
PRINT("duration", info->duration);
PRINT("id", info->_id);
PRINT("type", info->typeStr);
}
}
static void TestLibxml2(int count)
{
PRINT("=================== TestLibxml2 =================", "");
vector<thread> vts;
vts.reserve(count);
for (size_t i = 0; i < count; i++){
thread t1;
vts.push_back(thread(ReadXmlFunc));
}
for (auto& one : vts)
one.join();
}
int main()
{
// 应该在主线程初始化.否则在工作线程 xmlCleanupParser 应该配套使用.
xmlInitParser();
PRINT("Hello World: libxml2 version", LIBXML_DOTTED_VERSION);
TestLibxml2(1);
xmlCleanupParser();
// 以下会崩溃.
// 如果没有调用 xmlInitParser初始化,开启多线程使用 xmlReaderForIO 会崩溃.
//TestLibxml2(2);
}
test.xml
<?xml version="1.0" encoding="UTF-8"?>
<ARRAY><info>
<id>465</id>
<type>2</type>
<typeStr>Outgoing</typeStr>
<date>2018-04-13 14:21:12</date>
<duration>00:00</duration>
<name>first mid last</name>
<location>shenzhen</location>
<number>1234234</number>
</info><info>
<id>466</id>
<type>2</type>
<typeStr>Outgoing</typeStr>
<date>2018-04-13 14:21:07</date>
<duration>00:00</duration>
<name>first mid last</name>
<location></location>
<number>1234234</number>
</info><info>
<id>467</id>
<type>2</type>
<typeStr>Outgoing</typeStr>
<date>2018-04-13 14:21:03</date>
<duration>00:00</duration>
<name>Directory Assistance</name>
<location></location>
<number>411</number>
</info>
</ARRAY>
输出
Hello World: libxml2 version -> 2.7.1
=================== TestLibxml2 ================= ->
Read Size -> 3
================================= ->
name -> first mid last
location -> shenzhen
number -> 1234234
date -> 2018-04-13 14:21:12
duration -> 00:00
id -> 465
type -> Outgoing
================================= ->
name -> first mid last
location ->
number -> 1234234
date -> 2018-04-13 14:21:07
duration -> 00:00
id -> 466
type -> Outgoing
================================= ->
name -> Directory Assistance
location ->
number -> 411
date -> 2018-04-13 14:21:03
duration -> 00:00
id -> 467
type -> Outgoing
下载
https://download.csdn.net/download/infoworld/12326207