开发环境:Visual Studio 2010
运行环境:Windows XP SP3
第一节 读取XML文件(使用wchar_t[]字符数组)
/* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */
#define COBJMACROS
#include <stdio.h>
#include <MsXml6.h>
#pragma comment(lib, "msxml6.lib")
/*
参考资料:
(1) VARIANT结构体: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373687(v=vs.85).aspx
VARIANT用于表示一个弱类型的变量
(2) BSTR字符串: https://msdn.microsoft.com/en-us/library/windows/desktop/ms221069(v=vs.85).aspx
BSTR字符串是用于COM组件对象模型的字符串格式, 字符串以表示字符串长度的4字节整数开始, 然后跟上UTF-16编码的wchar_t字符串(包括\0结束标志)。BSTR类型的变量是一个指针, 指向字符串的第一个字符处
例如, 一段起始地址为1000的内存空间, 则1000~1003这四个字节存放字符串的长度, 1004开始才是字符串的真正内容, BSTR变量应指向1004而不是1000
wchar_t *用于保存UTF-16编码格式的字符串, 用wprintf的%ls输出
char *可以用来保存任意编码格式的字符串, 但只有ANSI格式才能使用printf的%s正确显示出来
在简体中文版操作系统下, ANSI就是GB2312编码
MultiByteToWideChar函数可以将char *字符串转换为wchar_t *字符串
WideCharToMultiByte函数可以将wchar_t *字符串转换为char *t字符串
SysAllocString函数可以将wchar_t *字符串转换为BSTR字符串
(3) IXMLDOMDocument接口: https://msdn.microsoft.com/en-us/library/windows/desktop/dd892951(v=vs.85).aspx
(4) 组件对象模型COM:
https://msdn.microsoft.com/en-us/library/windows/desktop/ff485848(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms680573(v=vs.85).aspx
(5) Windows窗口程序: https://msdn.microsoft.com/en-us/library/windows/desktop/ff381409(v=vs.85).aspx
*/
// C语言自带的wprintf函数无法打印中文
// 所以这里重新实现一个能打印中文的my_wprintf函数
int my_wprintf(const wchar_t *format, ...)
{
int n;
va_list list;
wchar_t str[1024];
DWORD ret;
HANDLE console;
va_start(list, format);
n = _vsnwprintf_s(str, _countof(str), _countof(str) - 1, format, list);
va_end(list);
console = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleW(console, str, (DWORD)wcslen(str), &ret, NULL);
return n;
}
// 显示元素节点的属性值 (元素节点是一种节点)
void display_attribute(IXMLDOMElement *elem, const wchar_t *name, int intval)
{
int num;
BSTR bstr; // 表示一个字符串
VARIANT variant; // 表示一个弱类型的变量
bstr = SysAllocString(name); // 必须用SysAllocString函数把wchar_t *字符串转换成BSTR字符串
IXMLDOMElement_getAttribute(elem, bstr, &variant);
SysFreeString(bstr); // 使用完字符串后必须释放
my_wprintf(L"[Attribute] name=%ls, value=%ls", name, variant.bstrVal); // BSTR和wchar_t *字符串都用%ls输出
if (intval)
{
num = _wtoi(variant.bstrVal); // 转换为整形
printf(", 2*value=%d", 2 * num);
}
printf("\n");
SysFreeString(variant.bstrVal); // 保存在Variant中的BSTR也必须释放掉
}
// 显示节点名称
void display_nodename(IXMLDOMNode *node)
{
BSTR bstr;
IXMLDOMNode_get_nodeName(node, &bstr);
my_wprintf(L"[Node] name=%ls\n", bstr); // 使用%ls打印BSTR字符串内容
SysFreeString(bstr); // 使用完字符串后必须释放
}
// 显示节点中的文本内容 (方法一)
void display_content(IXMLDOMNode *node)
{
BSTR text;
IXMLDOMNode_get_text(node, &text);
my_wprintf(L"[Text] %ls\n", text);
SysFreeString(text);
}
// 显示节点中的文本内容 (方法二)
// 文本内容是文本节点, 是node节点下的子节点
/*
void display_content(IXMLDOMNode *node)
{
IXMLDOMNode *child;
VARIANT value;
IXMLDOMNode_get_firstChild(node, &child); // 获取文本节点
IXMLDOMNode_get_nodeValue(child, &value); // 得到文本节点的内容
my_wprintf(L"[Text] %ls\n", value.bstrVal);
SysFreeString(value.bstrVal);
IXMLDOMNode_Release(child);
}
*/
// 获取节点中的XML文本内容
void display_inner_xml(IXMLDOMNode *node)
{
BSTR bstr;
IXMLDOMNode_get_xml(node, &bstr);
my_wprintf(L"[InnerXML] %ls\n", bstr);
SysFreeString(bstr);
}
void read_xml(IXMLDOMDocument *xmldoc)
{
long i, len;
IXMLDOMElement *root;
IXMLDOMElement *elem;
IXMLDOMNode *root_node;
IXMLDOMNode *item;
IXMLDOMNodeList *list;
// 根节点
IXMLDOMDocument_get_documentElement(xmldoc, &root);
IXMLDOMNode_QueryInterface(root, &IID_IXMLDOMNode, &root_node); // 把element node变为node
display_nodename(root_node);
display_attribute(root, L"id", 1);
display_inner_xml(root_node);
printf("\n");
// 根节点下的子节点
IXMLDOMElement_get_childNodes(root, &list);
IXMLDOMNodeList_get_length(list, &len);
for (i = 0; i < len; i++)
{
IXMLDOMNodeList_get_item(list, i, &item);
display_nodename(item);
display_content(item);
// 只有元素节点才有属性节点, 所以获取属性前要进行类型转换, 把node变为element node
IXMLDOMNode_QueryInterface(item, &IID_IXMLDOMElement, &elem);
display_attribute(elem, L"name", 0);
IXMLDOMElement_Release(elem);
IXMLDOMNode_Release(item);
}
IXMLDOMNodeList_Release(list);
IXMLDOMElement_Release(root_node);
IXMLDOMNodeList_Release(root);
}
// 打开XML文件
void open_xml(const wchar_t *name)
{
HRESULT hr;
IXMLDOMDocument *xmldoc;
VARIANT filename;
VARIANT_BOOL flag;
hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
if (SUCCEEDED(hr))
{
filename.bstrVal = SysAllocString(name); // 将wchar_t *转换为BSTR
filename.vt = VT_BSTR;
IXMLDOMDocument_load(xmldoc, filename, &flag);
// 注: IXMLDOMDocument_load是读XML文件
// IXMLDOMDocument_loadXML是读XML字符串
SysFreeString(filename.bstrVal);
if (flag == VARIANT_TRUE)
{
printf("读取XML文件成功\n");
read_xml(xmldoc);
}
else
printf("读取XML文件失败\n");
IXMLDOMDocument_Release(xmldoc);
}
}
int main()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型
open_xml(L"data.xml");
CoUninitialize();
return 0;
}
第二节 读取XML文件(使用char[]字符数组)
/* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */
#define COBJMACROS
#include <stdio.h>
#include <MsXml6.h>
#pragma comment(lib, "msxml6.lib")
// char *转BSTR
// 用完后调用SysFreeString释放
BSTR convert_string_to_bstr(const char *s)
{
int n;
wchar_t *ws;
BSTR bstr = NULL;
n = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);
ws = malloc(n * sizeof(wchar_t));
if (ws != NULL)
{
MultiByteToWideChar(CP_ACP, 0, s, -1, ws, n);
bstr = SysAllocString(ws);
free(ws);
}
return bstr;
}
// BSTR转char *
// 用完后调用free释放
char *convert_bstr_to_string(BSTR bstr)
{
char *s;
int n;
n = WideCharToMultiByte(CP_ACP, 0, bstr, -1, NULL, 0, NULL, NULL);
s = malloc(n);
if (s != NULL)
WideCharToMultiByte(CP_ACP, 0, bstr, -1, s, n, NULL, NULL);
return s;
}
// 显示元素节点的属性值 (元素节点是一种节点)
void display_attribute(IXMLDOMElement *elem, const char *name, int intval)
{
char *value_str;
int num;
BSTR bstr; // 表示一个字符串
VARIANT variant; // 表示一个弱类型的变量
bstr = convert_string_to_bstr(name);
IXMLDOMElement_getAttribute(elem, bstr, &variant);
SysFreeString(bstr); // 使用完字符串后必须释放
value_str = convert_bstr_to_string(variant.bstrVal);
printf("[Attribute] name=%s, value=%s", name, value_str);
if (intval)
{
num = atoi(value_str); // 转换为整形
printf(", 2*value=%d", 2 * num);
}
printf("\n");
free(value_str);
SysFreeString(variant.bstrVal); // 保存在Variant中的BSTR也必须释放掉
}
// 显示节点名称
void display_nodename(IXMLDOMNode *node)
{
char *s;
BSTR bstr;
IXMLDOMNode_get_nodeName(node, &bstr);
s = convert_bstr_to_string(bstr);
printf("[Node] name=%s\n", s);
free(s);
SysFreeString(bstr); // 使用完字符串后必须释放
}
// 显示节点中的文本内容 (方法一)
void display_content(IXMLDOMNode *node)
{
char *text_str;
BSTR text;
IXMLDOMNode_get_text(node, &text);
text_str = convert_bstr_to_string(text);
printf("[Text] %s\n", text_str);
free(text_str);
SysFreeString(text);
}
// 显示节点中的文本内容 (方法二)
// 文本内容是文本节点, 是node节点下的子节点
/*
void display_content(IXMLDOMNode *node)
{
char *value_str;
IXMLDOMNode *child;
VARIANT value;
IXMLDOMNode_get_firstChild(node, &child); // 获取文本节点
IXMLDOMNode_get_nodeValue(child, &value); // 得到文本节点的内容
value_str = convert_bstr_to_string(value.bstrVal);
printf("[Text] %s\n", value_str);
free(value_str);
SysFreeString(value.bstrVal);
IXMLDOMNode_Release(child);
}
*/
// 获取节点中的XML文本内容
void display_inner_xml(IXMLDOMNode *node)
{
char *s;
BSTR bstr;
IXMLDOMNode_get_xml(node, &bstr);
s = convert_bstr_to_string(bstr);
printf("[InnerXML] %s\n", s);
free(s);
SysFreeString(bstr);
}
void read_xml(IXMLDOMDocument *xmldoc)
{
long i, len;
IXMLDOMElement *root;
IXMLDOMElement *elem;
IXMLDOMNode *root_node;
IXMLDOMNode *item;
IXMLDOMNodeList *list;
// 根节点
IXMLDOMDocument_get_documentElement(xmldoc, &root);
IXMLDOMNode_QueryInterface(root, &IID_IXMLDOMNode, &root_node); // 把element node变为node
display_nodename(root_node);
display_attribute(root, "id", 1);
display_inner_xml(root_node);
printf("\n");
// 根节点下的子节点
IXMLDOMElement_get_childNodes(root, &list);
IXMLDOMNodeList_get_length(list, &len);
for (i = 0; i < len; i++)
{
IXMLDOMNodeList_get_item(list, i, &item);
display_nodename(item);
display_content(item);
// 只有元素节点才有属性节点, 所以获取属性前要进行类型转换, 把node变为element node
IXMLDOMNode_QueryInterface(item, &IID_IXMLDOMElement, &elem);
display_attribute(elem, "name", 0);
IXMLDOMElement_Release(elem);
IXMLDOMNode_Release(item);
}
IXMLDOMNodeList_Release(list);
IXMLDOMElement_Release(root_node);
IXMLDOMNodeList_Release(root);
}
// 打开XML文件
void open_xml(const char *name)
{
HRESULT hr;
IXMLDOMDocument *xmldoc;
VARIANT filename;
VARIANT_BOOL flag;
hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
if (SUCCEEDED(hr))
{
filename.bstrVal = convert_string_to_bstr(name); // 将char *转换为BSTR
filename.vt = VT_BSTR;
IXMLDOMDocument_load(xmldoc, filename, &flag);
// 注: IXMLDOMDocument_load是读XML文件
// IXMLDOMDocument_loadXML是读XML字符串
SysFreeString(filename.bstrVal);
if (flag == VARIANT_TRUE)
{
printf("读取XML文件成功\n");
read_xml(xmldoc);
}
else
printf("读取XML文件失败\n");
IXMLDOMDocument_Release(xmldoc);
}
}
int main()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型
open_xml("data.xml");
CoUninitialize();
return 0;
}
第三节 读取XML字符串(使用char[]字符数组)
// 读取XML字符串
void load_xml_str()
{
char *str = "<?xml version=\"1.0\" encoding=\"utf-8\"?><data id=\"20240117\"><post name=\"2023年人口统计数据发布了,大家怎么看?\">建档788W是主体,压哨建档出生的一部分,双胞胎三胞胎占一小部分,不建档生孩子的占一小部分,但是还是难凑出902,还是得预支一下。</post><post name=\"有什么是去了成都才知道的?\">不要跟团,也不要自驾游,成都每天都人满为患,人多车堵,还不好停车,景点大部分集中在1、2、3号线,坐地铁出行就能玩转成都。2、去鹤鸣茶社品一品茶,享受宁静时光感受成都的慢生活与惬意。3、成都火锅很辣,不是很能吃辣的朋友一定不要轻易尝试,不然会被辣的怀疑人生。</post><post name=\"能否找到一个f(x),使得当x取到全体正整数的时候,f(x)可以取到全体有理数?\">题主好像没有要求是单射啊……如果只要求满射的话,其实有个简单粗暴的做法,就是把正整数劈开……</post><post name=\"C++ 有什么好用的线程池?\">题主说的那两个thread pool其实都不错,但是2024年肯定是更推荐这一个,这个就是为进入标准的senders所实现的,学了也不会白学,当然它的使用非常简单。</post><post name=\"有什么适合大学生喝的性价比高的茶?\">虽然现代社会,买东西都在追求性价比,但茶叶这东西,我觉得还真不好说!</post></data>";
BSTR bstr;
HRESULT hr;
IXMLDOMDocument *xmldoc;
VARIANT_BOOL succeeded;
hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
if (SUCCEEDED(hr))
{
bstr = convert_string_to_bstr(str);
hr = IXMLDOMDocument_loadXML(xmldoc, bstr, &succeeded);
// 注: IXMLDOMDocument_load是读XML文件
// IXMLDOMDocument_loadXML是读XML字符串
SysFreeString(bstr);
if (SUCCEEDED(hr))
{
if (hr == S_OK)
{
printf("读取XML字符串成功\n");
read_xml(xmldoc);
}
else if (hr == S_FALSE)
printf("读取XML字符串失败\n");
}
// 另一种判断是否成功的方式
if (succeeded == VARIANT_TRUE)
printf("succeeded=true\n");
else if (succeeded == VARIANT_FALSE)
printf("succeeded=false\n");
IXMLDOMDocument_Release(xmldoc);
}
}
int main()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型
load_xml_str();
CoUninitialize();
return 0;
}
第四节 创建XML文件(使用char[]字符数组)
/* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */
#define COBJMACROS
#include <stdio.h>
#include <time.h>
#include <MsXml6.h>
#pragma comment(lib, "msxml6.lib")
// char *转BSTR
// 用完后调用SysFreeString释放
BSTR convert_string_to_bstr(const char *s)
{
int n;
wchar_t *ws;
BSTR bstr = NULL;
n = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);
ws = malloc(n * sizeof(wchar_t));
if (ws != NULL)
{
MultiByteToWideChar(CP_ACP, 0, s, -1, ws, n);
bstr = SysAllocString(ws);
free(ws);
}
return bstr;
}
// BSTR转char *
// 用完后调用free释放
char *convert_bstr_to_string(BSTR bstr)
{
char *s;
int n;
n = WideCharToMultiByte(CP_ACP, 0, bstr, -1, NULL, 0, NULL, NULL);
s = malloc(n);
if (s != NULL)
WideCharToMultiByte(CP_ACP, 0, bstr, -1, s, n, NULL, NULL);
return s;
}
void create_header(IXMLDOMDocument *xmldoc)
{
BSTR target, data;
IXMLDOMNode *header_node;
IXMLDOMProcessingInstruction *header;
// 创建XML文档头节点
target = convert_string_to_bstr("xml");
data = convert_string_to_bstr("version=\"1.0\" encoding=\"utf-8\"");
IXMLDOMDocument_createProcessingInstruction(xmldoc, target, data, &header);
SysFreeString(target);
SysFreeString(data);
// header转换成node, 然后添加进文档
// 注意旧的header对象和新产生的node对象都要释放
IXMLDOMProcessingInstruction_QueryInterface(header, &IID_IXMLDOMNode, &header_node);
IXMLDOMProcessingInstruction_Release(header);
IXMLDOMDocument_appendChild(xmldoc, header_node, NULL); // 最后一个参数是输出参数, 没有用
IXMLDOMNode_Release(header_node);
}
IXMLDOMElement *create_root(IXMLDOMDocument *xmldoc)
{
char timestr[30];
struct tm tm;
time_t t;
BSTR name;
IXMLDOMElement *root;
IXMLDOMNode *root_node;
VARIANT value;
// 创建根节点
name = convert_string_to_bstr("data");
IXMLDOMDocument_createElement(xmldoc, name, &root);
SysFreeString(name);
// 将创建的根节点添加到文档中
IXMLDOMElement_QueryInterface(root, &IID_IXMLDOMNode, &root_node);
IXMLDOMDocument_appendChild(xmldoc, root_node, NULL);
IXMLDOMNode_Release(root_node);
// 获取当前时间
time(&t);
localtime_s(&tm, &t);
strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", &tm);
// 在根节点上添加一个属性
name = convert_string_to_bstr("timestr");
value.bstrVal = convert_string_to_bstr(timestr);
value.vt = VT_BSTR;
IXMLDOMElement_setAttribute(root, name, value);
SysFreeString(name);
SysFreeString(value.bstrVal);
return root;
}
void add_post(IXMLDOMDocument *xmldoc, IXMLDOMElement *root, const char *name, const char *text)
{
BSTR attrname_bstr;
BSTR nodename_bstr;
BSTR text_bstr;
IXMLDOMElement *elem;
IXMLDOMNode *node;
VARIANT value;
nodename_bstr = convert_string_to_bstr("post");
IXMLDOMDocument_createElement(xmldoc, nodename_bstr, &elem); // 创建element
SysFreeString(nodename_bstr);
IXMLDOMElement_QueryInterface(elem, &IID_IXMLDOMNode, &node); // element转node
IXMLDOMElement_appendChild(root, node, NULL);
// 添加name属性
attrname_bstr = convert_string_to_bstr("name");
value.bstrVal = convert_string_to_bstr(name);
value.vt = VT_BSTR;
IXMLDOMElement_setAttribute(elem, attrname_bstr, value);
SysFreeString(attrname_bstr);
SysFreeString(value.bstrVal);
// 添加节点内文本
text_bstr = convert_string_to_bstr(text);
IXMLDOMElement_put_text(elem, text_bstr);
SysFreeString(text_bstr);
// 释放element和node对象
// 请注意element是node的子类, 换句话说element是一种node
IXMLDOMElement_Release(elem);
IXMLDOMNode_Release(node);
}
void write_xml(IXMLDOMDocument *xmldoc)
{
IXMLDOMElement *root;
create_header(xmldoc);
root = create_root(xmldoc);
add_post(xmldoc, root, "RPG Maker XP", "RPG Maker XP(RPGXP)是一款可让玩家自行制作在计算机游戏中相当受欢迎的角色扮演游戏,也就是 Role-Playing Game(RPG)的软件。制作完成的游戏,即使在没有安装 RPG Maker XP 的电脑上也能运行。");
add_post(xmldoc, root, "高精度的画面", "支持 640×480 像素分辨率 32 色真彩色图像。完全对应 Alpha Channel(每个像素的透明度),光滑的透过处理,柔和的半透明表现。画面的合成方法也增加了通常的半透明合成、加算合成和减算合成。而且字型自动映射,粗体不再引人注目。");
add_post(xmldoc, root, "使用软件的音乐播放", "作为 BGM,主要使用 DirectMusic Synthesizer 的 MIDI 播放。因为以软件播放,所以不能用其它格式代替,音乐素材的制造人没必需考虑对各种 MIDI 音乐的对应。在制作过程中能被正常播放的 MIDI 文件,同样可以在其它环境中正常播放。");
add_post(xmldoc, root, "灵活且强大的脚本系统", "用 RPGXP 制作完成的游戏,脚本是以 Ruby 语言编写的。预先编写的脚本可以制作十分独特和有趣的游戏,它是为了更高级的游戏制作需求准备的,使用它可以进行画面设计、编写战斗系统,以及一切的游戏要素。运用脚本制作 RPG 以外类型的游戏也是可能的。");
add_post(xmldoc, root, "魔塔样板", "魔塔样板是一个用RPG Maker XP、RPG Maker VX、RPG Maker Vx Ace或者VB制作的一个用来制作魔塔的样板,里面有魔塔里一些基本的地图和怪物和各种宝物。制作者只需改动怪物属性和地图,其他的都可以利用复制进行制作,制作起来方便。并且制作者还可以在魔塔样板中进行更高级的操作,做出一些特别的动画或者机关,总之,魔塔样版可以制作一切类型的魔塔。但是,有些质量较差的魔塔样板会有脚本错误。最常用版本是7630。");
add_post(xmldoc, root, "房贷利息", "房贷利息是指每个月支付给银行的利息款,计算公式为:剩余未还房款×贷款利率%÷12。若贷款100万元,利率为5.88%,则第一个月要付的利息钱是100万×5.88%÷12=4900元。也就是说“钱价”为4900元/(百万元·月)。");
IXMLDOMElement_Release(root);
}
// 创建XML文件
void create_xml(const char *name)
{
HRESULT hr;
IXMLDOMDocument *xmldoc;
VARIANT filename;
hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
if (SUCCEEDED(hr))
{
write_xml(xmldoc);
filename.bstrVal = convert_string_to_bstr(name); // 将char *转换为BSTR
filename.vt = VT_BSTR;
hr = IXMLDOMDocument_save(xmldoc, filename);
SysFreeString(filename.bstrVal);
if (SUCCEEDED(hr))
printf("写入XML文件成功\n");
else
printf("写入XML文件失败\n");
IXMLDOMDocument_Release(xmldoc);
}
}
int main()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型
create_xml("myxmlfile.xml");
CoUninitialize();
return 0;
}