Protobuf:复杂类型接口
Protobuf:复杂类型接口
- package
- 字段规则
- 复杂类型
- enum
- Any
- oneof
- map
本博客基于proto3语法,讲解protobuf中的复杂类型。
package
在.proto文件中,支持导入其它.proto文件的内容,例如:
test.proto:
syntax = "proto3";
package test_pkg;import "other.proto";message Person {int32 id =1;string name = 2;other_pkg.Address addr = 3;
}
other.proto:
syntax="proto3";
package other_pkg;message Address {string country = 1;string city = 2;
}
在test.proto文件中,使用了other.proto的内容。想要实现多个文件的效果,需要通过improt导入文件:
import 文件名.proto
随后在导入的文件中,使用包名.变量名的形式使用变量,比如other_pkg.Address。
字段规则
如果想在protobuf中实现一个数组,它使用了一种字段规则的修饰方式来实现数组。
消息字段类型可以用以下规则修饰:
singular:消息中该字段可以出现0次或1次,字段默认使用该规则repeated:消息中该字段可以出现任意次数,包括0次
简单来说,对于singular变量只能存储一个值,如果输入新的值,后来的值会把原先的值覆盖。而对于repeated变量,其可以存储多个值,所以值都会被保留,其实也就是数组。
示例:
syntax = "proto3";
package test_pkg;message Person {int32 id = 1;string name = 2;repeated string phone = 3;
}
此处的phone字段被设置为了repeated,可以理解为一个string的数组,在pb.h文件中,该字段包含以下接口:
// repeated string phone = 3;
int phone_size() const;
private:
int _internal_phone_size() const;
public:
void clear_phone();
const std::string& phone(int index) const;
std::string* mutable_phone(int index);
void set_phone(int index, const std::string& value);
void set_phone(int index, std::string&& value);
void set_phone(int index, const char* value);
void set_phone(int index, const char* value, size_t size);
std::string* add_phone();
void add_phone(const std::string& value);
void add_phone(std::string&& value);
void add_phone(const char* value);
void add_phone(const char* value, size_t size);
可以看到,相比于一般的string类型,被repeated修饰后,多出了很多下标操作,这里挑几个接口出来讲解:
int phone_size() const:获取当前数组的长度。const std::string& phone(int index) const:通过下标获取值的操作,获取该变量的const引用。std::string* mutable_phone(int index):获取指定下标元素的指针,可以通过指针修改该元素。
void set_phone(int index, const std::string& value);
void set_phone(int index, std::string&& value);
void set_phone(int index, const char* value);
void set_phone(int index, const char* value, size_t size);
这四个接口用于设置指定下标的值,四种函数重载效果是一样的,只是传入字符串的形式不同。
std::string* add_phone():数组末尾增加一个空元素,并返回指向该元素的指针,可以通过指针修改该元素。
void add_phone(const std::string& value);
void add_phone(std::string&& value);
void add_phone(const char* value);
void add_phone(const char* value, size_t size);
这四个接口直接插入一个指定元素到数组末尾,重载是为了兼容不同形式的字符串。
被repeat修饰的变量接口总结:
| 接口 | 功能 |
|---|---|
xxx_size() | 获取数组的长度 |
xxx(index) | 获取指定下标的元素,不可修改 |
muteble_xxx(index) | 获取指定下标元素的指针,可修改 |
set_xxx(index, value) | 设置指定下标的元素为value |
add_xxx() | 尾插一个元素到数组,返回指向该元素的指针,可修改 |
add_xxx(value) | 尾插value到数组 |
复杂类型
enum
enum是枚举类型,其列举多个常量,方便后续使用。
定义一个枚举,表示有哪些人物种类:
enum Type{STU = 0;TEACHER = 1;OTHER = 2;
}
此处枚举了三个常量,分别表示学生,老师,其它。
枚举类型有以下要求:
0值必须存在,且要作为第一个元素- 在语言中,定义枚举变量后,如果指定变量值,第一个元素
0是默认值 - 枚举的常量值在
32位整数内,但是负数无效
示例:
syntax = "proto3";
package test_pkg;enum Type{STU = 0;TEACHER = 1;OTHER = 2;
}message Person {int32 id = 1;string name = 2;repeated string phone = 3;Type type = 4;
}
编译后,在.pb.h文件可以找到以下枚举类型:
enum Type : int {STU = 0,TEACHER = 1,OTHER = 2,Type_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),Type_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
};
此处使用了C++11的强枚举类型语法,enum Type : int 指定底层类型为int。除去在.proto文件中指定的三个成员,还增加了两个其它成员,最后两个成员用于做边界值判断,限制成员的值只在int32范围内。
紧接着的是部分枚举类型的专有接口:
bool Type_IsValid(int value);
constexpr Type Type_MIN = STU;
constexpr Type Type_MAX = OTHER;
constexpr int Type_ARRAYSIZE = Type_MAX + 1;const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Type_descriptor();
template<typename T>
inline const std::string& Type_Name(T enum_t_value)
inline bool Type_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, Type* value);
Type_IsValid:判断整数是否是enum内部的成员之一Type_Name:输入枚举值,返回枚举值对应的字符串名称Type_parse:输入字符串和枚举值,判断两者是否匹配
再往后,在Person类中,定义了枚举的通用接口:
// .test_pkg.Type type = 4;
void clear_type();
::test_pkg::Type type() const;
void set_type(::test_pkg::Type value);
可以看到,枚举类型的get和set接口还是比较简单的。
enum接口总结:
| 接口 | 功能 |
|---|---|
xxx_IsValid() | 判断整数是否是enum内部的成员之一 |
xxx_Name(value) | 输入枚举值,返回枚举值对应的字符串名称 |
xxx_Parse(string, value) | 输入字符串和枚举值,判断两者是否匹配 |
xxx() | 获取当前枚举变量的值 |
set_xxx(value) | 设置枚举的值 |
Any
假设现在要给Person类定义一个info字段,用于存储该人的具体信息。但是由于Person可以是学生,可以是老师,这两种人存储的info是不同的,如何让一个字段存储不同的值,成为一个泛型?
此处Any类型就排上用场了,Any可以存储任何其它类型的message,相当于一个泛型。
syntax = "proto3";
package test_pkg;import "google/protobuf/any.proto";message test{google.protobuf.Any info = 1;
}
Any的本质是一个message,写在google/protobuf/any.proto中,使用Any类型需要import导包。在类内使用类型时,也需要指定包名google.protobuf.Any。
引入泛型后,当前Person类如下:
syntax = "proto3";
package test_pkg;import "google/protobuf/any.proto";enum Type{STU = 0;TEACHER = 1;OTHER = 2;
}message stuInfo {string className = 1; // 班级int32 score = 2; // 成绩
}message teacherInfo {string subject = 1; // 科目int32 jobAge = 2; // 工龄
}message Person {int32 id = 1;string name = 2;repeated string phone = 3;Type type = 4;google.protobuf.Any info = 5;
}
Person的info字段用于存储具体信息,对于学生来说,要存储班级和成绩。而对于老师来说,要存储所教的科目和工龄。此时将这些信息分别定义在stuInfo和teacherInfo中,在Person内引入一个Any字段info,这个info就可以存储任意其它的message,自然也包括stuInfo和teacherInfo。
编译后,在.pb.h中Any的接口如下:
// .google.protobuf.Any info = 5;
bool has_info() const;
private:
bool _internal_has_info() const;
public:
void clear_info();
const ::PROTOBUF_NAMESPACE_ID::Any& info() const;
PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Any* release_info();
::PROTOBUF_NAMESPACE_ID::Any* mutable_info();
void set_allocated_info(::PROTOBUF_NAMESPACE_ID::Any* info);
has_info():检查该字段是否为空clear_info():清空该字段的值info():获取该字段的值release_info():获取字段内的值后,清空字段内的值mutable_info():返回指向元素的指针,可通过指针修改元素
另外的,还有几个重要的接口,在google/protobuf/any.pb.h中:
bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message);bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message* message);template<typename T>
bool Is();
这三个接口是Any最核心的接口:
PackFrom:将一个message类型转化为Any类型UnpackTo:将一个Any类型转化回messageIs:传入模板参数,判断当前Any存储的类型是否和模板参数一样
这个用法有点复杂,示例:
#include <iostream>
#include "test.pb.h"using namespace std;
using namespace test_pkg;int main()
{test_pkg::Person ps;ps.set_id(123456);ps.set_name("张三");// repeat字段,可以设置多个值ps.add_phone("123456");ps.add_phone("654321");ps.set_type(Type::STU); // 学生类型stuInfo stuinfo;stuinfo.set_classname("六年级一班");stuinfo.set_score(95);google::protobuf::Any any_message;if (any_message.PackFrom(stuinfo)) *ps.mutable_info() = any_message;stuInfo getStuinfo;if (ps.info().Is<stuInfo>())ps.info().UnpackTo(&getStuinfo);return 0;
}
首先初始化了一个Person,随后初始化了该对象的初始信息,确定该对象是一个学生。所以他的info要存储一个stuInfo类型,用于表示详细信息。
stuInfo stuinfo;
stuinfo.set_classname("六年级一班");
stuinfo.set_score(95);
这段代码初始化了一个stuinfo对象。
随后将这个stuInfo对象设置到ps.info成员中:
google::protobuf::Any any_message;
if (any_message.PackFrom(stuinfo)) *ps.mutable_info() = any_message;
设置Any成员时,首先要定义一个Any对象,然后通过PackFrom函数,把stuInfo对象转化为Any对象。如果PackFrom返回true,说明转化成功,此时any_message内部就已经是stuInfo的内容了。最后通过*ps.mutable_info() = any_message设置info字段为any_message对象。
如果后续想要把Any对象变回stuInfo对象:
stuInfo getStuinfo;
if (ps.info().Is<stuInfo>())ps.info().UnpackTo(&getStuinfo);
定义一个getStuinfo接收返回值。随后执行ps.info().Is<stuInfo>(),判断当前的info内存储的变量类型是不是stuInfo,如果是,那么执行ps.info().UnpackTo(&getStuinfo),将info的内容解析到getStuinfo中。
不论用Any存储或提取任何一个message类型,都是这一套逻辑。
Any接口总结:
| 接口 | 功能 |
|---|---|
has_xxx() | 检查该字段是否为空 |
clear_xxx() | 清空该字段的值 |
xxx() | 获取该字段的值 |
release_xxx() | 获取字段内的值后,清空字段内的值 |
mutable_xxx() | 返回指向元素的指针,可通过指针修改元素 |
PackFrom(message) | 将一个message类型转化为Any类型 |
UnpackTo(&message) | 将一个Any类型转化回message |
Is<messageType>() | 传入模板参数,判断当前Any存储的类型是否和模板参数一样 |
oneof
有时候,message中的字段在多个中选择一个,就可以使用oneof类型。
示例:
message Person {int32 id = 1;string name = 2;oneof contact {int32 qq = 3;int32 tel = 4;}
}
此处的Person,增加了一个联系方式contact,用户可以在qq和tel中二选一。
在oneof内部的字段编号,不能与外部的message冲突,比如qq和tel的字段编号就不可以是1和2。
另外的,在oneof内部,不允许使用repeated修饰字段,比如这样:
message Person {int32 id = 1;string name = 2;oneof contact {repeated int32 qq = 3; // errrepeated int32 tel = 4; // err}
}
如果设置了repeated,该修饰会失效,变量依然只能保存一个值。
对以下protoc代码编译:
message Person {int32 id = 1;string name = 2;oneof contact {int32 qq = 3;int32 tel = 4;}
}
结果:
// int32 qq = 3;
bool has_qq() const;
private:
bool _internal_has_qq() const;
public:
void clear_qq();
int32_t qq() const;
void set_qq(int32_t value);public:
// int32 tel = 4;
bool has_tel() const;
private:
bool _internal_has_tel() const;
public:
void clear_tel();
int32_t tel() const;
void set_tel(int32_t value);void clear_contact();
看起来这个oneof好像没有生效?此处就好像是单独定义了qq和tel两个字段,也没有什么oneof的相关方法。
其实这个地方,qq和tel确实是直接当作两个字段处理的,但是两个字段有点像C语言中的联合体,最后一次设置的是那个变量,那么存储的就是哪一个类型。
示例:
test_pkg::Person ps;
ps.set_id(123456);
ps.set_name("张三");
ps.set_qq(111111);
ps.set_tel(222222);if (ps.has_qq())cout << "qq: " << ps.qq() << endl;if (ps.has_tel())cout << "tel: " << ps.tel() << endl;
定义了一个Person变量后,先后设置了qq(111111)和tel(222222),随后通过has_xxx方法分别检测两个值,随后输出。
输出结果:
tel: 222222
最后只输出了tel,因为后来的tel覆盖了qq,两者是二选一的关系。
当然,oneof类型也不是完全没有接口,比如这个接口:
void clear_contact();
在需要清除oneof内部的值的时候,如果不确定具体是哪一个类型,就无法确定是caler_qq还是clear_tel,如果一个个通过has判断还是有点麻烦的,就可以通过clear_contact一键清除。
所有的oneof的字段都被保存在一个枚举中:
enum ContactCase {kQq = 3,kTel = 4,CONTACT_NOT_SET = 0,
};ContactCase contact_case() const;
此处的kQq和kTel分别就是之前设置的qq和tel,而CONTACT_NOT_SET表示未设置参数。
其实在判断当前oneof存储的是哪一个字段时,不用一个个进行has_xxx的判断。通过contact_case函数,会返回一个枚举类型,只需要判断枚举类型是哪一个变量即可:
switch (ps.contact_case())
{case Person::ContactCase::kQq:// 处理qq字段break;case Person::ContactCase::kTel:// 处理tel字段break;case Person::ContactCase::CONTACT_NOT_SET:// 没有字段被设置break;
}
oneof接口总结:
| 接口 | 功能 |
|---|---|
clear_xxx() | 不论字段存储的是什么,清空该字段的值 |
xxx_case() | 返回当前存储的类型的枚举值 |
map
map用于创建一个键值对的映射关系,语法:
map<key_tyep, value_type> map_name = N;
其实语法和C++的std::map是一样的。
map有以下注意点:
key_type:可以是处理float和bytes之外的任意标量类型value_type:可以是任意类型map中的元素是无序的
示例:
message score {map<string, int32> chinese = 1;map<string, int32> english = 2;map<string, int32> math = 3;
}
这是一个记录学生成绩的表格,三个成员分别代表不同科目的成绩。
编译结果以chinese为例:
// map<string, int32> chinese = 1;
int chinese_size() const;
private:
int _internal_chinese_size() const;
public:
void clear_chinese();
private:
const ::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >&_internal_chinese() const;
::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >*_internal_mutable_chinese();
public:
const ::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >&chinese() const;
::PROTOBUF_NAMESPACE_ID::Map< std::string, int32_t >*mutable_chinese();
chinese_size():获取map内部元素的个数clear_chinese():清空map的元素chinese():获取map的const引用mutable_chinese():获取map的指针,可通过指针修改map
此处map的类型不是std::map,而是protobuf自己封装的::PROTOBUF_NAMESPACE_ID::Map,但是其用法和std::map没有很大区别。
::PROTOBUF_NAMESPACE_ID::Map也重载了operator[],拿到变量后,可以直接通过[]进行访问。
示例:
test_pkg::Score sc;auto chinese_mp = *sc.mutable_chinese();
auto english_mp = *sc.mutable_english();
auto math_mp = *sc.mutable_math();chinese_mp["张三"] = 66;
chinese_mp["李四"] = 88;
chinese_mp["王五"] = 98;english_mp["张三"] = 97;
english_mp["李四"] = 77;
english_mp["王五"] = 82;math_mp["张三"] = 46;
math_mp["李四"] = 25;
math_mp["王五"] = 79;cout << "张三-chinese: " << chinese_mp["张三"] << endl;
cout << "张三-english: " << english_mp["张三"] << endl;
cout << "张三-math: " << math_mp["张三"] << endl;
通过auto chinese_mp = *sc.mutable_chinese(),获取到map,由于mutable_chinese返回的是一个指针,所以还要进行解引用。
随后通过chinese_mp[] 设置与获取元素了。
输出结果:
张三-chinese: 66
张三-english: 97
张三-math: 46
map接口总结:
| 接口 | 功能 |
|---|---|
xxx_size() | 获取map内部元素的个数 |
clear_xxx() | 清空map的元素 |
xxx() | 获取map的const引用 |
mutable_xxx() | 获取map的指针,可通过指针修改map |
map.operator[] | 直接通过[]获取与设置元素 |
相关文章:
Protobuf:复杂类型接口
Protobuf:复杂类型接口 package字段规则复杂类型enumAnyoneofmap 本博客基于proto3语法,讲解protobuf中的复杂类型。 package 在.proto文件中,支持导入其它.proto文件的内容,例如: test.proto: syntax …...
Git Push 深度解析:命令的区别与实践
目录 命令一:git push origin <branch-name>命令二:git push Factory_sound_detection_tool test工作流程:两者的主要区别实践中的应用总结 Git 是一种分布式版本控制系统,它允许用户对代码进行版本管理。在 Git 中…...
大数据开发基础实训室设备
大数据实验实训一体机 大数据实验教学一体机是一种专为大数据教育设计的软硬件融合产品,其基于华为机架服务器进行了调优设计,从而提供了卓越的性能和稳定性。这一产品将企业级虚拟化管理系统与实验实训教学信息化平台内置于一体,通过软硬件…...
【数据结构】string(C++模拟实现)
string构造 string::string(const char* str):_size(strlen(str)) {_str new char[_size 1];_capacity _size;strcpy(_str, str); }// s2(s1) string::string(const string& s) {_str new char[s._capacity 1];strcpy(_str, s._str);_size s._size;_capacity s._cap…...
【笔记】I/O总结王道强化视频笔记
文章目录 从中断控制器的角度来理解整个中断处理的过程复习 处理器的中断处理机制**中断驱动I/O方式** printf——从系统调用到I/O控制方式的具体实现1轮询方式下输出一个字符串(程序查询)中断驱动方式下输出一个字符串中断服务程序中断服务程序与设备驱动程序之间的关系 DMA方…...
XML XSLT:转换与呈现数据的力量
XML XSLT:转换与呈现数据的力量 XML(可扩展标记语言)和XSLT(XML样式表转换语言)是现代信息技术中不可或缺的工具,它们在数据交换、存储和呈现方面发挥着重要作用。本文将深入探讨XML和XSLT的概念、应用及其在信息技术领域的重要性。 XML:数据交换的标准 XML是一种用于…...
ES6总结
1.let和const以及与var区别 1.1 作用域 var: 变量提升(Hoisting):var 声明的变量会被提升到其作用域的顶部,但赋值不会提升。这意味着你可以在声明之前引用该变量(但会得到 undefined)。 con…...
晶体匹配测试介绍
一、晶体参数介绍 晶体的电气规格相对比较简单,如下: 我们逐一看看每个参数, FL就是晶体的振动频率,这个晶体是24.576MHz的。 CL就是负载电容,决定了晶体频率是否准确,包括外接的实际电容、芯片的等效电容以及PCB走线的寄生电容等,核心参数。 Frequency Tolerance是…...
超声波清洗机靠谱吗?适合学生党入手的四款眼镜清洗机品牌推荐!
有没有学生党还不知道双十一买什么?其实可以去看看超声波清洗机,说实话它的实用性真的很高,对于日常用于清洗眼镜真的非常合适,不仅可以帮助大家节约时间而且还能把眼镜清洗的干净透亮,接下来我就来为大家带来四款好用…...
Java生成图片_基于Spring AI
Spring AI 优势 过去,使用Java编写AI应用时面临的主要困境是没有统一且标准的封装库,开发者需自行对接各个AI服务提供商的接口,导致代码复杂度高、迁移成本大。如今,Spring AI Alibaba的出现极大地缓解了这一问题,它提…...
程序传入单片机的过程,以Avrdude为例分析
在市场上有各式各样的单片机,例如Arduino,51单片机,STM等。通常,我们都用其对应的IDE软件进行单片机的编程。这些软件既负责将程序代码转写成二进制代码,即机器语言,也负责将该二进制代码导入单片机。与此同…...
用YOLO和LLM增强的OCR
虽然最近我花了很多时间在大型语言模型 (LLM) 上进行实验,但我对计算机视觉的热情始终未减。因此,当我有机会将两者融合在一起时,我迫不及待地想要立即开始。在 Goodreads 上扫描书籍封面并将其标记为已读一直感觉有点神奇,我很兴…...
开源的云平台有哪些?
开源云平台为用户提供了构建、管理和运行云基础设施及应用的能力,同时允许社区参与开发和改进。以下是一些知名的开源云平台: 1. OpenStack 简介:OpenStack:一个广泛使用的开源云平台,它由多个组件组成,提…...
Spring Boot学习资源库:微服务架构的加速器
3 系统分析 3.1可行性分析 在进行可行性分析时,我们通常根据软件工程里方法,通过四个方面来进行分析,分别是技术、经济、操作和法律可行性。因此,在基于对目标系统的基本调查和研究后,对提出的基本方案进行可行性分析。…...
Pi4+wfb-ng+8812au
sudo apt update将如下文件拷入树莓派4 linux-6f921e98008589258f97243fb6658d09750f0a2f.tar.gz libsodium.zip rtl8812au.zip wfb-ng_Pi4_2.zip 安装libsodium unzip libsodium.zip cd libsodium ./configure make && make check sudo make install#安装8812AU驱动 …...
基于单片机的非接触智能测温系统设计
本设计主要由单片机STC8A8K64S4A12、OLED显示屏、非接触式测温模块MLX9061、无线通讯模块ESP8266以及声光报警模块等构成。系统通过非接触式测温模块MLX9061测量当前人员温度,温度通过OLED显示屏进行实时显示,当被测温度高于阈值,声光报警模块…...
第二十三篇:网络拥塞了,TCP/IP如何解决的?
一.显示拥塞通知 当发生网络拥塞时,发送主机应该减少数据包的发送量。作为IP上层协议,TCP虽然也能控制网络拥塞,不过它是通过数据包的实际损坏情况来判断是否发生拥塞。然而这种方法不能在数据包损坏之前减少数据包的发送量。 为了解决这个…...
登录注册静态网页实现(HTML,CSS)
实现效果图 实现效果 使用HTML编写页面结构,CSS美化界面,点击注册,跳转到注册界面,均为静态网页,是课上的一个小作业~ 使用正则表达式对输入进行验证,包括邮箱格式验证,用户名格式验证。 正则…...
基于FPGA的以太网设计(二)
一.以太网硬件架构概述 前文讲述了以太网的一些相关知识,本文将详细讲解以太网的硬件架构 以太网的电路架构一般由MAC、PHY、变压器、RJ45和传输介质组成,示意图如下所示: PHY:Physical Layer,即物理层。物理层定义了…...
OJ在线评测系统 后端微服务架构 注册中心 Nacos入门到启动
注册中心 服务架构中的注册中心是一个关键组件,用于管理和协助微服务之间的通信。注册中心的主要职责是服务的注册和发现,确保各个微服务能够相互找到并进行调用。 主要功能: 服务注册:微服务在启动时,将自身信息&am…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理:检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目:RankRAG:Unifying Context Ranking…...
