C++解析xml示例
C++解析xml示例
- 1. Xml文档介绍
- 1.1 特点及作用
- 1.2 Xml优点
- 1.2.1 良好的可拓展性
- 1.2.2 内容与形式分离
- 1.3 Xml组成
- 1.3.1 Xml声明
- 1.3.2 根元素
- 1.3.3 元素
- 1.3.4 属性
- 1.3.5 实体
- 1.3.6 注释
- 2 C++解析Xml
- 2.1 tinyXml2类库
- 2.2 关键接口
- 2.2.1 LoadFile
- 2.2.2 RootElement
- 2.2.3 FirstChildElement
- 2.2.4 NextSiblingElement
- 2.2.5 Value
- 2.2.6 Attribute
- 2.2.7 ToDeclaration
- 3 搜索Xml中数据案例
1. Xml文档介绍
Xml(Extensible Markup Language)即可扩展标记语言,Xml是互联网数据传输的重要工具,它可以跨越互联网任何的平台,不受编程语言和操作系统的限制,可以说它是一个拥有互联网最高级别通行证的数据携带者。
Xml用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。Xml是标准通用标记语言(SGML)的子集,非常适合Web传输。Xml提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。
在很多工具或服务中需要解析Xml,此文档供相关开发人员使用C++解析Xml做参考。
1.1 特点及作用
Xml特点:
- xml与操作系统、编程语言的开发平台都无关;
- 实现不同系统之间的数据交互;
Xml作用: - 配置应用程序和网站,在配置文件里几乎所有的配置文件都是以Xml的格式来编写的;
- 数据交互,即服务器以XML的形式传输数据;
- 储存数据;
1.2 Xml优点
1.2.1 良好的可拓展性
在XML产生之前,要想定义一个置标语言并推广利用它非常困难。一方面,如果制定了一个新的语言而期望它能生效,需要把这个标准提交给相关的组织(如W3C),等待它接受并正式公布这个标准,经过几轮的评定和修改,到这个置标语言终于成为一个正式推荐标准时,可能已经过了几年的时间。另一方面,为了让这套标记得到广泛应用,制订者必须为它配备浏览工具。这样,就不得不去游说各个浏览器厂商接受并支持新制定的标记,或者索性自己开发一个新的浏览器去与现有的浏览器竞争,无论哪个办法,都需要耗费大量的时间和工作。现在借助XML的帮助,制定新的置标语言要简单易行得多了,这也正是XML的优势所在。
各个不同的行业可能会有一些独特的要求,比如说,化学家需要化学公式中的一些特殊符号,建筑家需要设计图纸中的某些特殊的标记,音乐家需要音符,这些都需要单独的标记。但是,其他网页设计者一般不会用这些记号,也不需要这些标记。XML的优点就在于它允许各个组织、个人建立适合他们自己需要的标记库,并且这个标记库可以迅速地投入使用。
1.2.2 内容与形式分离
XML不仅允许自定义一套标记,而且这些标记不必仅限于对显示格式的描述。在XML中,显示样式从数据文档中分离出来,放在样式单文件中。这样,如果要改动信息的表现方式,无须改动信息本身,只要改动样式单文件就够了。如果这时候把列表的数据改用表格显示,无须再去修改大量数据信息文档,因为它们和同一个样式文件相关联,只要改动这个样式单文件就可以了。
在XML中数据搜索可以简单高效地进行。 若在HTML文件中搜索某个字符串,脚本会逐字寻找对应的字符串,即使找到了,若存在多个相同目标字符串,也不能确定是否为自己想要找到的那个。在XML中搜索不必要再去遍历整个文档,只需要找一下相关标记下的内容。此外,信息之间的某些复杂关系,比如树状结构、继承关系等,在XML中也都得到了绝好的体现,这样就大大方便了XML的应用处理程序的开发。
1.3 Xml组成
一个正规的Xml文档的结构需要有声明,文档元素描述信息(有且仅有一个根元素),元素属性。也可能包含实体,注释等信息。
1.3.1 Xml声明
在编写XML文档时,需要先使用文档声明来声明XML文档。且必须出现在文档的第一行。最简单的声明语法:
<?xml version="xxx"?>
Version后引号内的字符串表示文档的版本。
当我们写好的一个xml文件写入内存的时候会转换为二进制保存,这个时候会查码表,记事本保存的时候是gbk,而保存的时候默认查码表时用的是utf-8,这个时候我们就可以用encoding属性:默认是UTF-8
<?xml version="xxx” encoding="GBK"?>
这样就可以解决乱码等问题。
Xml声明还可能包含standlone属性:该xml文件是否独立存在。
1.3.2 根元素
根元素是Xml文档里面唯一的,它的开始是放在最前面,结束是放在最后面。
1.3.3 元素
根元素下的所有元素格式与根元素相同,其格式为:
Note:
- 所有元素都必须有开始标签和结束标签;
- 开始标签和结束标签的名称必须相同(区分大小写);
- 元素title名称可以包含字母,数字或者其他字符,但是不能以数字或者符号开头,名称中不能包含空格;
- 名称字符之间不能使用冒号(有特殊用途);
1.3.4 属性
对于有属性的元素,语法结构为:
<元素名 属性名=“属性值”></元素名>
Note:
- 属性值用双引号包括,一个元素可以有多个属性;
- 属性值中不能直接包含符号;
1.3.5 实体
在xml中,一些字符拥有特殊的意义。如果把字符“<”放在xml元素中,会发生错误,这是因为解析器会把它当作新元素的开始,这样会产生xml错误。
为了避免这个错误,在元素中需要引用这些字符时,用实体引用来代替字符。Xml中5个预定义实体:
实体 | 符号 |
---|---|
&It; | < |
> | > |
& | & |
" | * |
' | . |
1.3.6 注释
注释语法:
<!—注释内容–>
Note:
- 注释内容中不要出现“–”;
- 注释不能嵌套;
- XML声明之前不能有注释,不允许第一行写注释;
2 C++解析Xml
2.1 tinyXml2类库
在C++中读写Xml会用到一个C++库tinyXml2类库。这个库中包含了我们读取Xml所需的接口。
TinyXML2是一个开源、简单、小巧、高效的C++ XML解析器,可以轻松集成到其它程序中。目前存在两个著名的开源XML文件解析库:tinyXml1,tinyXml2。相对tinyXml1而言,tinyXml2更为简洁,它只有一个tinyXml2.h文件和一个tinyXml2.cpp文件组成,而tinyXml1包含了6个文件。
TinyXml2具有以下几个优点:
- 对大部分的C/C++项目具有普适性,并且拥有丰富的测试案例;
- 使用较少的内存(约 TinyXML1 的 40%),速度变得更快;
- 没有C++的STL要求;
- 更接近现代 C++ 的特性,如使用了适当的命名空间;
- 适当有效的处理了的空白字符(空格,Tab 和回车);
使用方法:将 tinyxml2.cpp 和 tinyxml2.h 拷贝至项目目录,使用时包含头文件和引入名字空间。
2.2 关键接口
2.2.1 LoadFile
作用:用于加载Xml文件
传参:LoadFile(FilePath)
一般使用格式:
if(Xml.LoadFile(FilePath)!=XML_SUCCESS)// 通过Xml文件路径读取文件,若读取失败则返回return;
2.2.2 RootElement
作用:用于读取Xml的根节点
传参:RootElement()
一般使用格式:
tinyxml2::XMLElement* rootElement = NULL;
rootElement=Xml.RootElement();//rootElement指向当前Xml的根节点
if(rootElement==NULL)//若读取失败则返回
return;
2.2.3 FirstChildElement
作用:用于读取xml节点的下一层节点
传参:(1)FirstChildElement()//读取下一层节点的第一个节点
(2)FirstChildElement(“XXX”)//读取下一层节点第一个元素名为“XXX”的节点
一般使用格式:
const XMLNode* Element1 = NULL;
Element1 = rootElement-> FirstChildElement(“XXX”);//Element1指向rootElement的下一层第一个元素名为”XXX”的节点
if(Element1 ==NULL)//若读取失败则返回
return;
2.2.4 NextSiblingElement
作用:用于读取Xml节点同层节点的下一个节点
传参:NextSiblingElement()
一般使用格式:
const XMLNode* Element2 = NULL;
Element2 = Element1 -> NextSiblingElement ();//Element2指向Element1的同层节点的下一个节点
if(Element2 ==NULL)//若读取失败则返回
return;
2.2.5 Value
作用:用于读取Xml节点的元素名
传参:Value()
一般使用格式:
String str1= Element2->Value();//将Element2的元素名赋值给str1
2.2.6 Attribute
作用:用于读取Xml节点的属性值
传参:Attribute()
一般使用格式:
String str2 = Element2->ToElement()->Attribute(“XXX”);//将Element2的属性名为”XXX”的属性值赋值给str2
2.2.7 ToDeclaration
作用:用于读取Xml文件的声明
传参:ToDeclaration()
一般使用格式:
tinyxml2::XMLDocument xmlParse;
if (xmlParse.LoadFile(str_xmlPath.c_str()) != tinyxml2::XML_SUCCESS)return -1;
XMLNode *decl= xmlParse.FirstChild();
if(NULL!= decl){XMLDeclaration* declaration =decl->ToDeclaration();
string strDecl = declaration->Value();//最终将声明字符串赋值给strDecl
}
3 搜索Xml中数据案例
通常在Xml中搜索数据会有很多限制条件,需要通过层层搜索找到想要的数据。
如图中,若希望通过限制条件WwanDeviceConfigID-ID,FCC_MAP_INDEX_TYPE- Value,Standard- Vaule,Item- PSensors/ DeviceMode/ AntMode/ AntMode1找到对应的Item –SarIndex,代码案例如下,是一个对应的从Xml中搜索数据的接口(代码部分,仅供参考):
int CStateWork::GetSarIndexFromXml(string str_xmlPath, string WwanDeviceConfigID, string regmcc, string PSensors, string DeviceMode, string AntMode, string AntMode1)string SarIndex;
do{
1.初始化tinyxml2::XMLDocument xmlParse;tinyxml2::XMLElement* rootElement = NULL;const XMLNode* WwanDeviceConfigIDElement = NULL;const XMLNode* WwanDeviceConfigIDList = NULL;const XMLNode* FCC_MAP_INDEX_TYPE_Element = NULL;const XMLNode* MapTypeElement = NULL;const XMLNode* StandardElement = NULL;const XMLNode* ItemElement = NULL;string FCC_MAP_INDEX_TYPE;
int i_ret = -1;
2.加载Xml文件,并获取根节点if (xmlParse.LoadFile(str_xmlPath.c_str()) != tinyxml2::XML_SUCCESS){return i_ret;}rootElement = xmlParse.RootElement();if (NULL == rootElement){return i_ret;}
3.通过传参WwanDeviceConfigID,找到对应的WwanDeviceConfigID节点WwanDeviceConfigIDList = rootElement->FirstChildElement("WwanDeviceConfigIDList");if (NULL == WwanDeviceConfigIDList){return i_ret;}WwanDeviceConfigIDElement = WwanDeviceConfigIDList->FirstChildElement("WwanDeviceConfigID");for (WwanDeviceConfigIDElement; WwanDeviceConfigIDElement; WwanDeviceConfigIDElement = WwanDeviceConfigIDElement->NextSiblingElement()){if (WwanDeviceConfigIDElement->ToElement()->Attribute("ID") == WwanDeviceConfigID)break;}4.在WwanDeviceConfigID节点下,找到FCC_MAP_INDEX_TYPE节点的属性值FCC_MAP_INDEX_TYPE_Element = WwanDeviceConfigIDElement->FirstChildElement("FCC_MAP_INDEX_TYPE");if (NULL == FCC_MAP_INDEX_TYPE_Element){return i_ret;}FCC_MAP_INDEX_TYPE = FCC_MAP_INDEX_TYPE_Element->ToElement()->Attribute("Value");5.在WwanDeviceConfigID节点下,找到与FCC_MAP_INDEX_TYPE节点的属性值对应的MapTypeMapTypeElement = WwanDeviceConfigIDElement->FirstChildElement(FCC_MAP_INDEX_TYPE.c_str());if (NULL == MapTypeElement){return i_ret;}6.在MapType节点下,找到属性值与传参regmcc对应的Standard节点StandardElement = MapTypeElement->FirstChildElement("Standard");if (NULL == StandardElement){return i_ret;}for (StandardElement; StandardElement; StandardElement = StandardElement->NextSiblingElement()){if (StandardElement->ToElement()->Attribute("Vaule") == regmcc)break;}7.在Standard节点下,找到属性值与传参PSensors,DeviceMode,AntMode,AntMode1一一对应的Item节点(不同的MapType属性个数不同,需要先判断在哪个MapType下搜索)ItemElement = StandardElement->FirstChildElement("Item");if (NULL == ItemElement){return i_ret;}if (FCC_MAP_INDEX_TYPE.find("MapType_1") != std::string::npos){ItemElement = StandardElement->FirstChildElement();if (NULL == ItemElement){return i_ret;}}else if (FCC_MAP_INDEX_TYPE.find("MapType_2") != std::string::npos){if (PSensors == ""){return i_ret;}ItemElement = StandardElement->FirstChildElement();if (NULL == ItemElement){return i_ret;}while (ItemElement->ToElement()->Attribute("PSensors") != PSensors){ItemElement = ItemElement->NextSiblingElement();if (NULL == ItemElement){return i_ret;}}}else if (FCC_MAP_INDEX_TYPE.find("MapType_3") != std::string::npos){if (DeviceMode == ""){return i_ret;}ItemElement = StandardElement->FirstChildElement();if (NULL == ItemElement){return i_ret;}while (ItemElement->ToElement()->Attribute("DeviceMode") != DeviceMode){ItemElement = ItemElement->NextSiblingElement();if (NULL == ItemElement){return i_ret;}}}else if (FCC_MAP_INDEX_TYPE.find("MapType_4") != std::string::npos){if (PSensors == ""){return i_ret;}if (DeviceMode == ""){return i_ret;}ItemElement = StandardElement->FirstChildElement();if (NULL == ItemElement){return i_ret;}while (ItemElement->ToElement()->Attribute("PSensors") != PSensors|| ItemElement->ToElement()->Attribute("DeviceMode") != DeviceMode){ItemElement = ItemElement->NextSiblingElement();if (NULL == ItemElement){return i_ret;}}}else if (FCC_MAP_INDEX_TYPE.find("MapType_5") != std::string::npos){if (PSensors == ""){return i_ret;}if (AntMode == ""){ return i_ret;}if (AntMode1 == ""){return i_ret;}ItemElement = StandardElement->FirstChildElement();if (NULL == ItemElement){return i_ret;}while (ItemElement->ToElement()->Attribute("PSensors") != PSensors|| ItemElement->ToElement()->Attribute("AntMode") != AntMode|| ItemElement->ToElement()->Attribute("AntMode1") != AntMode1){ItemElement = ItemElement->NextSiblingElement();if (NULL == ItemElement){return i_ret;}}}else if (FCC_MAP_INDEX_TYPE.find("MapType_6") != std::string::npos){if (PSensors == ""){return i_ret;}if (AntMode == ""){return i_ret;}if (AntMode1 == ""){return i_ret;}if (DeviceMode == ""){return i_ret;}ItemElement = StandardElement->FirstChildElement();if (NULL == ItemElement){return i_ret;}while (ItemElement->ToElement()->Attribute("PSensors") != PSensors|| ItemElement->ToElement()->Attribute("AntMode") != AntMode|| ItemElement->ToElement()->Attribute("AntMode1") != AntMode1|| ItemElement->ToElement()->Attribute("DeviceMode") != DeviceMode){ItemElement = ItemElement->NextSiblingElement();if (NULL == ItemElement){return i_ret;}}}8.在Item节点下,读出SarIndex属性值,并作为最终接口的输出SarIndex = ItemElement->ToElement()->Attribute("SarIndex");} while (FALSE);return atoi(SarIndex.c_str());
}
Note:
- 在LoadFile Xml文件时,应该判断是否成功打开,若失败则返回;
- 在获取任何新的节点以及节点指向的位置变动后,应该判断当前节点是否为空,若为空则应该返回或做其他异常处理;
- 当前节点往下向子节点搜寻,一般直接用FirstChild函数直接实现,当前节点往下向同级节点搜寻,一般用NextSiblingElement函数遍历实现;
- 在同一个元素中可能会有多组属性和属性值,在这种情况下搜索相应的数据一般需要用循环,对于每一次循环都是获取一次新的节点,因此在循环前和每一次循环后都应该判断当前节点是否为空,以免出现crash
相关文章:

C++解析xml示例
C解析xml示例 1. Xml文档介绍1.1 特点及作用1.2 Xml优点1.2.1 良好的可拓展性1.2.2 内容与形式分离 1.3 Xml组成1.3.1 Xml声明1.3.2 根元素1.3.3 元素1.3.4 属性1.3.5 实体1.3.6 注释 2 C解析Xml2.1 tinyXml2类库2.2 关键接口2.2.1 LoadFile2.2.2 RootElement2.2.3 FirstChildE…...
记录 | linux find+rm查找并直接删除
findrm查找并直接删除: find ./ -name "xx_name" |xargs rm -rf...
24.有哪些生命周期回调方法?有哪几种实现方式?
有哪些生命周期回调方法?有哪几种实现方式? 有两个重要的bean 生命周期方法, 第一个是init , 它是在容器加载bean的时候被调用。第二个方法是 destroy 它是在容器卸载类的时候被调用。bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始…...
C++详解
//7.用new建立一个动态一维数组,并初始化int[10]{1,2,3,4,5,6,7,8,9,10},用指针输出,最后销毁数组所占空间。 #include<iostream> #include<string> using namespace std; int main() { int *p; pnew int[10]; for(i…...

mybatis数据输入-实体类型的参数
1、建库建表 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_emp(emp_id INT AUTO_INCREMENT,emp_name CHAR(100),emp_salary DOUBLE(10,5),PRIMARY KEY(emp_id) );INSERT INTO t_emp(emp_name,emp_salary) VALUES("tom",200.33); INSERT INTO…...
Java-接口
目录 定义 格式 使用 接口中成员的特点 成员变量 构造方法 成员方法 JDK8新特性:可以定义有方法体的方法 默认方法 作用 定义格式 注意事项 静态方法 定义格式 注意事项 JDK9新特性:可以定义私有方法 私有方法的定义格式 接口和接口之…...
mysql常用命令行代码
连接到 MySQL 服务器: mysql -u your_username -p替换 your_username 为你的 MySQL 用户名。系统会提示你输入密码。 退出 MySQL 命令行: EXIT;或者按 Ctrl D。 显示所有数据库: SHOW DATABASES;选择数据库: USE your_database…...
Python压缩、解压文件
#!/usr/bin/python3 # -*- coding:utf-8 -*- """ author: JHC file: util_compress.py time: 2023/5/28 14:58 desc: rarfile 使用需要安装 rarfile 和 unrar 并且将 unrar.exe 复制到venv/Scrpits目录下 (从WinRar安装目录下白嫖的) 下载…...

面试就是这么简单,offer拿到手软(一)—— 常见非技术问题回答思路
面试系列: 面试就是这么简单,offer拿到手软(一)—— 常见非技术问题回答思路 面试就是这么简单,offer拿到手软(二)—— 常见65道非技术面试问题 文章目录 一、前言二、常见面试问题回答思路问…...

134. 加油站(贪心算法)
根据题解 这道题使用贪心算法,找到当前可解决问题的状态即可 「贪心算法」的问题需要满足的条件: 最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较大的问题的解只由其中一个规模较小的子问题的解决定ÿ…...

Springboot3+vue3从0到1开发实战项目(二)
前面完成了注册功能这次就来写登录功能, 还是按照这个方式来 明确需求: 登录接口 前置工作 : 想象一下登录界面(随便在百度上找一张) 看前端的能力咋样了, 现在我们不管后端看要什么参数就好 阅读接口文档…...

Spring中Bean的生命周期
1.生命周期 Spring应用中容器管理了我们每一个bean的生命周期,为了保证系统的可扩展性,同时为用户提供自定义的能力,Spring提供了大量的扩展点。完整的Spring生命周期如下图所示,绿色背景的节点是ApplictionContext生命周期特有的…...
IndexOutOfBoundsException: Index: 2048, Size: 2048] Controller接收对象集合长度超过2048错误
完整异常信息: org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [spring] in context with path [/jsgc] threw exception [Request processing failed; nested exception is org.springframework.beans.InvalidPropertyExce…...

2023年中国消费金融行业研究报告
第一章 行业概况 1.1 定义 中国消费金融行业,作为国家金融体系的重要组成部分,旨在为消费者提供多样化的金融产品和服务,以满足其消费需求。这一行业包括银行、消费金融公司、小额贷款公司等多种金融机构,涵盖了包括消费贷款在内…...

深度学习:什么是知识蒸馏(Knowledge Distillation)
1 概况 1.1 定义 知识蒸馏(Knowledge Distillation)是一种深度学习技术,旨在将一个复杂模型(通常称为“教师模型”)的知识转移到一个更简单、更小的模型(称为“学生模型”)中。这一技术由Hint…...

【Go】protobuf介绍及安装
目录 一、Protobuf介绍 1.Protobuf用来做什么 2. Protobuf的序列化与反序列化 3. Protobuf的优点和缺点 4. RPC介绍 <1>文档规范 <2>消息编码 <3>传输协议 <4>传输性能 <5>传输形式 <6>浏览器的支持度 <7>消息的可读性和…...
c语言编程题经典100例——(41~45例)
1,实现动态内存分配。 在C语言中,动态内存分配使用malloc、calloc、realloc和free函数。以下是一个示例: #include <stdio.h> #include <stdlib.h> int main() { int *ptr NULL; // 初始化为空 int n 5; // 假设我们想要分配5个整数…...

计算机毕业设计|基于SpringBoot+MyBatis框架健身房管理系统的设计与实现
计算机毕业设计|基于SpringBootMyBatis框架的健身房管理系统的设计与实现 摘 要:本文基于Spring Boot和MyBatis框架,设计并实现了一款综合功能强大的健身房管理系统。该系统涵盖了会员卡查询、会员管理、员工管理、器材管理以及课程管理等核心功能,并且…...

java学习part27线程死锁
基本就是操作系统的内容 138-多线程-线程安全的懒汉式_死锁_ReentrantLock的使用_哔哩哔哩_bilibili...

(二)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
一、无人机模型简介: 单个无人机三维路径规划问题及其建模_IT猿手的博客-CSDN博客 参考文献: [1]胡观凯,钟建华,李永正,黎万洪.基于IPSO-GA算法的无人机三维路径规划[J].现代电子技术,2023,46(07):115-120 二、Tiki-taka算法(TTA…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...