首页 > 其他分享 >【ProtoBuf】语法详解

【ProtoBuf】语法详解

时间:2024-10-18 20:17:18浏览次数:3  
标签:ProtoBuf PeopleInfo contacts phone 语法 Phone 详解 message string

protoBuf的基础使用可参看ProtoBuf基础使用

本篇博客依旧以通讯录为例展开讲解,语法为proto3
当前通讯录属性如下:

message PeopleInfo{
	string name = 1;
	int32 age = 2;
}

经过学习,实现通讯录如下功能:

  • 新增联系人属性,共包括:姓名,年龄,电话信息,地址,其他联系方式,备注
  • 将通讯录序列化后写入文件
  • 从文件中将通讯录解析出来,并打印

文章目录

字段规则

消息字段可以用如下两种规则修饰:

  • sigular:消息中可以包含该字段零次或一次(至多一次)。proto3语法中,字段默认使用该规则
  • repeated:消息中可以包含该字段任意次(包括零次),其中重复值的顺序会被保留。可以理解为定义了一个数组

使用repeated新增phone_numbers,表示一个联系人可以有多个号码,可以将其设置为repeated

syntax = "proto3";
package contacts;

message PeopleInfo{
	string name = 1;
	int32 age = 2;
	repeated string phone_numbers = 3;
}

常用API

  • 获取元素个数
inline int PeopleInfo::phone_size()
//示例:
PeopleInfo people;
int n = people.phone_size();
  • 通过下标访问
inline const std::string& PeopleInfo::phone(int index)
//示例:
PeopleInfo people;
for(int i = 0; i < people.phone_size(); ++i)
	auto phone = people.phone(i);
  • 添加元素
//返回开辟好的空间
inline std::string* PeopleInfo::add_phone_numbers()
//示例:
PeopleInfo people;
std::string *name = people->add_phone_numbers();
name1 = &string("你好");

消息类型的定义和使用

定义

在单个 .proto 文件中可以定义多个消息体,且支持定义嵌套类型的消息(任意多层)。每个消息体的字段编号可以重复

更新 contacts.proto ,将 phone_number 设置成一个消息,包含电话号码

syntax = "proto3";
package contacts;

//嵌套写法
message PeopleInfo{
	string name = 1;
	int32 age = 2;
	message Phone{
        string number = 1;
    }
}



//非嵌套写法
message Phone{
    string number = 1;
}
message PeopleInfo{
	string name = 1;
	int32 age = 2;
}

使用

  • 消息类型可作为字段类型使用
syntax = "proto3";
package contacts;

//嵌套写法
message PeopleInfo{
	string name = 1;
	int32 age = 2;
	message Phone{//定义
        string number = 1;
    }
    repeated Phone phone = 3;//使用
}



//非嵌套写法
message Phone{//定义
    string number = 1;
}
message PeopleInfo{
	string name = 1;
	int32 age = 2;
    repeated Phone phone = 3;//使用
}

也可以单独编写 .proto 文件,然后导入
将 Phone 消息定义在 phone.proto文件中
phone.proto

syntax = "proto3";
package phone;

message Phone{
    string number = 1;
}

contact.proto

syntax = "proto3";
package contacts;

import "phone.proto //导入

message PeopleInfo{
	string name = 1;
	int32 age = 2;
	
    repeated phone.Phone phone = 3;//需要指明package
}

更新通讯录

syntax = "proto3";
package contacts;

//嵌套写法
message PeopleInfo{
	string name = 1;    //姓名
	int32 age = 2;      //年龄

	message Phone{
        string number = 1;
    }
    repeated Phone phone = 3;   //电话  
}

message Contacts{   
    repeated PeopleInfo peoples = 1;    //多个联系人
}

总结:

  • 每个字段都有一个 clear_ 方法,可以将字段重新设置回 empty 状态
  • 每个字段都有设置和获取方法,获取字段的方法名称和小写字段名称完全相同。但如果是消息类型的字段,其设置方法为 mutable_ 方法,返回值为消息类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进行修改
  • 对于 repeated 修饰的字段,也就是数组类型,protoBuf 为我们提供了 add_ 方法来新增一个值,并且提供了 _size 方法来判断数组存放元素的个数

读写通讯录
write.cc:用于获取联系人信息,序列化,然后写入文件

#include<iostream>
#include<fstream>

#include"contacts.pb.h"

using namespace std;
using namespace contacts;
/**新增联系⼈*/
voidAddPeopleInfo(PeopleInfo *people_info_ptr)
{
	cout <<"-------------新增联系⼈-------------"<< endl;
	cout <<"请输⼊联系⼈姓名: ";string name;
	getline(cin, name);
	people_info_ptr->set_name(name);
	cout <<"请输⼊联系⼈年龄: ";intage;
	cin >> age;
	people_info_ptr->set_age(age);
	cin.ignore(256,'\n');
	for(inti =1; ; i++) {
		cout <<"请输⼊联系⼈电话"<< i <<"(只输⼊回⻋完成电话新增): ";
		string number;
		getline(cin, number);
		if(number.empty())
			break;
		PeopleInfo_Phone* phone = people_info_ptr->add_phone();
		phone->set_number(number);
	}
	cout <<"-----------添加联系⼈成功-----------"<< endl;
}

intmain(intargc,char*argv[])
{
	// GOOGLE_PROTOBUF_VERIFY_VERSION宏:
	//验证没有意外链接到与编译的头⽂件不兼容的库版本。
	//如果检测到版本不匹配,程序将中⽌。注意,每个.pb.cc⽂件在启动时都会⾃动调⽤此宏。
	//在使⽤C++ Protocol Buffer库之前执⾏此宏是⼀种很好的做法,但不是绝对必要的。
	GOOGLE_PROTOBUF_VERIFY_VERSION;
	if(argc !=2){
		cerr <<"Usage:  "<< argv[0] <<" CONTACTS_FILE"<< endl;
		return-1;
	}
	Contacts contacts;
	//先读取已存在的contacts
	fstreaminput(argv[1], ios::in | ios::binary);
	if(!input)
		cout << argv[1] <<": File not found.  Creating a new file."<< endl;
	else if(!contacts.ParseFromIstream(&input)) {
		cerr <<"Failed to parse contacts."<< endl;
		input.close();
		return-1;
	}
	//新增⼀个联系⼈
	AddPeopleInfo(contacts.add_contacts());
	//向磁盘⽂件写⼊新的contacts
	fstreamoutput(argv[1], ios::out | ios::trunc | ios::binary);
	if(!contacts.SerializeToOstream(&output)){
		cerr <<"Failed to write contacts."<< endl;
		input.close();
		output.close();
		return-1;
	}
	input.close();
	output.close();
	//在程序结束时调⽤ShutdownProtobufLibrary(),
	//为了删除Protocol Buffer库分配的所有全局对象。对于⼤多数程序来说这是不必要的,
	//因为该过程⽆论如何都要退出,并且操作系统将负责回收其所有内存。
	//但是,如果你使⽤了内存泄漏检查程序,该程序需要释放每个最后对象,
	//或者你正在编写可以由单个进程多次加载和卸载的库,
	//那么你可能希望强制使⽤Protocol Buffers来清理所有内容。
	google::protobuf::ShutdownProtobufLibrary();
	return 0;
}

makefile

write:write.cc contacts.pb.cc
	g++ -o $@ $^ -std=c++11 -lprotobuf
.PHONY:clean
clean:
	rm -f write

运行结果如下:

hyb@139-159-150-152:~/project/protobuf/contacts$ make
g++ -o write write.cc contacts.pb.cc -std=c++11 -lprotobufhyb
@139-159-150-152:~/project/protobuf/contacts$ ./write contacts.bin
contacts.bin: File not found.  Creating a new file.
-------------新增联系⼈-------------
请输⼊联系⼈姓名:张三
请输⼊联系⼈年龄: 20
请输⼊联系⼈电话1(只输⼊回⻋完成电话新增): 13111111111
请输⼊联系⼈电话2(只输⼊回⻋完成电话新增): 15111111111
请输⼊联系⼈电话3(只输⼊回⻋完成电话新增):
-----------添加联系⼈成功-----------

查看二进制文件
hexdump:是Linux下的⼀个⼆进制⽂件查看⼯具,它可以将⼆进制⽂件转换为ASCII、⼋进制、
⼗进制、⼗六进制格式进⾏查看。
-C:表⽰每个字节显⽰为16进制和相应的ASCII字符

hyb@139-159-150-152:~/project/protobuf/contacts$hexdump -C contacts.bin
00000000  0a 28 0a 06 e5 bc a0 e4  b8 89 10 14 1a 0d 0a 0b  |.(..............|
00000010  31 33 31 31 31 31 31 31  31 31 31 1a 0d 0a 0b 31  |13111111111....1|
00000020  35 31 31 31 31 31 31 31  31 31                    |5111111111|0000002a

read.cc:用于从文件中读取数据,反序列化,然后打印

#include <iostream>
#include <fstream>

#include "contacts.pb.h"
using namespace std;
using namespace contacts;
/**打印联系⼈列表*/
voidPrintfContacts(constContacts& contacts) 
{
	for(inti =0; i < contacts.contacts_size(); ++i) 
	{
		const PeopleInfo& people = contacts.contacts(i);
		cout <<"------------联系⼈"<< i+1<<"------------"<< endl;cout <<"姓名:"<< people.name() << endl;
		cout <<"年龄:"<< people.age() << endl;intj =1;
		for(const PeopleInfo_Phone& phone : people.phone())
			cout <<"电话"<< j++ <<": "<< phone.number() << endl;
	}
}
int main(intargc,char* argv[]) 
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;
	if(argc !=2) {
		cerr <<"Usage:  "<< argv[0] <<"CONTACTS_FILE"<< endl;
		return-1;
	}
	//以⼆进制⽅式读取contacts
	Contacts contacts;
	fstreaminput(argv[1], ios::in | ios::binary);
	if(!contacts.ParseFromIstream(&input)) {
		cerr <<"Failed to parse contacts."<< endl;
		input.close();
		return-1;
	}
	//打印contacts
	PrintfContacts(contacts);
	input.close();
	google::protobuf::ShutdownProtobufLibrary();
	return0;
}

更新makefile

all:write read
write:write.cc contacts.pb.cc
	g++ -o $@ $^ -std=c++11 -lprotobuf
read:read.cc contacts.pb.cc
	g++ -o $@ $^ -std=c++11 -lprotobuf
.PHONY:clean
clean:
	rm -f write read

运行结果:

hyb@139-159-150-152:~/project/protobuf/contacts$ make
g++ -o read read.cc contacts.pb.cc -std=c++11 -lprotobufhyb
@139-159-150-152:~/project/protobuf/contacts$ ./read contacts.bin
------------联系⼈1------------
姓名:张三
年龄:20
电话1:13111111111
电话2:15111111111

enum类型

定义

语法支持我们定义枚举类型并使用。在 .proto 文件中枚举类型的书写规范为:

枚举类型名称:
	使用驼峰命名法,首字母大写。例如:MyEnum
常量值名称:
	全大写字母,多个字母之间用 _ 连接。例如:ENUM_CONST = 0;

我们定义一个电话类型的枚举类型:

enum PhoneType{
	MP = 0;   //移动电话
	TEL = 1;  //固定电话
}

枚举类型的定义规则如下:

  1. 0值常量必须存在,且要作为一个元素。这是为了和 proto2 语义兼容:第一个元素作为默认值,且值为0
  2. 枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)
  3. 枚举的常量值在 32 位整数范围,负值无效不建议使用

定义时注意

将两个 “具有相同枚举值名称” 的枚举类型放在单个 .proto 文件下时,编译会报错

  • 同级(同层)的枚举类型,各个枚举类型中的常量不能重名
  • 单个 .proto 文件下,最外层枚举类型和嵌套枚举类型,不算同级
  • 多个 .proto 文件下,若一个文件引入其他文件,且每个文件都未声明 package,那么每个 .proto 文件最外层的枚举类型都算同级
  • 多个 .proto 文件,若都有声明 package,则不算同级

更新通讯录

为电话号码添加电话类型(枚举类型)

syntax = "proto3";
package contacts;

message PeopleInfo{
	string name = 1;    //姓名
	int32 age = 2;      //年龄

	message Phone{
        string number = 1;
        enum PhoneType{
        	MP = 0; //移动电话
        	TEL = 1; //固定电话	
        }
        PhoneType type = 2; //类型
    }
    repeated Phone phone = 3;   //电话  
}

message Contacts{   
    repeated PeopleInfo peoples = 1;    //多个联系人
}

编译后形成的 .h文件

//新⽣成的PeopleInfo_Phone_PhoneType枚举类
enumPeopleInfo_Phone_PhoneType:int{
	PeopleInfo_Phone_PhoneType_MP =0,
	PeopleInfo_Phone_PhoneType_TEL =1,
	
PeopleInfo_Phone_PhoneType_PeopleInfo_Phone_PhoneType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::min(),
PeopleInfo_Phone_PhoneType_PeopleInfo_Phone_PhoneType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<int32_t>::max()
};
//更新的PeopleInfo_Phone类
public:
typedef PeopleInfo_Phone_PhoneType PhoneType;
static inline bool PhoneType_IsValid(intvalue) {
 return PeopleInfo_Phone_PhoneType_IsValid(value);
}
template<typenameT>
static inline const std::string& PhoneType_Name(T enum_t_value){...}
static inline bool PhoneType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PhoneType* value) {...}
// .contacts.PeopleInfo.Phone.PhoneType type = 2;
	voidclear_type();
	::contacts::PeopleInfo_Phone_PhoneTypetype()const;
	voidset_type(::contacts::PeopleInfo_Phone_PhoneType value);
};

接口:

  • 枚举类型编译生成的代码中会含有与之对应的枚举类型,校验枚举值是否有效的方法 _IsVaild、以及获取枚举值名称的方法 _Name
  • 对于使用了枚举类型的字段,包含设置和获取方法,以及清空字段的方法 clear_

write.cc 在添加电话中新增关于电话类型的选择

//输入电话
for(int i = 0; ; ++i)
{
    //电话号码
    std::string phone;
    std::cout << "请输入联系人电话(仅输入回车表示结束): ";
    getline(std::cin, phone);
    if(phone.empty())
        break;//仅输入回车
    contacts2::PeopleInfo_Phone *people_phone = people->add_phone();
    people_phone->set_phone(phone);
    //电话类型
    std::cout << "请输入电话类型(1. 移动电话 2. 固定电话): ";
    int type = 0;
    std::cin >> type;
    std::cin.ignore(256, '\n');
    //判断电话类型
    switch(type)
    {
        case 1:
            people_phone->set_type(contacts2::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);
            break;
        case 2:
            people_phone->set_type(contacts2::PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);
            break;
        default:
            std::cout << "输入有误!" << std::endl;
            break;
    }
}

read.cc 新增打印电话类型

//电话
for(int j = 0; j < people.phone_size(); ++j)
{
    const contacts2::PeopleInfo_Phone &phone = people.phone(j);
    std::cout << j + 1 << "电话: " << phone.phone()
              << "  (" << phone.PhoneType_Name(phone.type()) << ")" << std::endl;
}

Any 类型

字段还可以声明为 Any 类型,可以理解为泛型类型。使用时可以在 Any 中存储任意消息类型。Any 类型的字段也可以使用 repeated 修饰
Any 是 google 定义的类型,使用时需要包含相应 .proto文件

更新通讯录

添加地址信息,在PeopleInfo 中使用 Any 接收

syntax = "proto3";
package contacts;

import "goole/protobuf/any.proto"

//地址
message Address
{
    string host_address = 1;    //家庭地址
    string unit_address = 2;    //单位地址
}

message PeopleInfo{
	string name = 1;    //姓名
	int32 age = 2;      //年龄

	message Phone{
        string number = 1;
        enum PhoneType{
        	MP = 0; //移动电话
        	TEL = 1; //固定电话	
        }
        PhoneType type = 2; //类型
    }
    repeated Phone phone = 3;   //电话  
    google.protobuf.Any data = 4;
}

message Contacts{   
    repeated PeopleInfo peoples = 1;    //多个联系人
}

接口:

  • 对于Any 类型字段,获取方法的方法名称同小写字段名称设置方法使用 mutable_ 方法,返回值为 Any 类型的指针,这类方法会返回开辟好的空间,可以直接对该控件内容进行修改
  • Any 类型可接收任意类型,互相转化的方法在any.pb.h
class PROTOBUF_EXPORT Any final : public::PROTOBUF_NAMESPACE_ID::Message {
	boolPackFrom(const::PROTOBUF_NAMESPACE_ID::Message& message) {
		...
	}
	boolUnpackTo(::PROTOBUF_NAMESPACE_ID::Message* message)const{
		...
	}
	template<typename T>boolIs()const{
		return_impl_._any_metadata_.Is<T>();
	}
};
解释:
使⽤PackFrom()⽅法可以将任意消息类型转为Any类型。
使⽤UnpackTo()⽅法可以将Any类型转回之前设置的任意消息类型。
使⽤Is()⽅法可以⽤来判断存放的消息类型是否为typename<T>。

write.cc新增添加地址

//家庭地址
contacts2::Address address;
std::cout << "请输入家庭地址:";
std::string host_address;
getline(std::cin, host_address);
if(!host_address.empty())
    address.set_host_address(host_address);
    
std::cout << "请输入单位地址:";
std::string unit_address;
getline(std::cin, unit_address);
if(!unit_address.empty())
    address.set_unit_address(unit_address);
    
//使用泛型Any接收
google::protobuf::Any *data = people->mutable_data();
data->PackFrom(address);

read.cc新增打印地址

//地址
//泛型有数据,并且类型匹配
if(people.has_data() && people.data().Is<contacts2::Address>())
{
    contacts2::Address address;
    people.data().UnpackTo(&address);
    if(!address.host_address().empty())
        std::cout << "家庭地址: " << address.host_address() << std::endl;
    if(!address.unit_address().empty())
        std::cout << "单位地址: " << address.unit_address() << std::endl; 
}

oneof 类型

如果消息中有很多可选字段,并且将来同时只有一个字段会被设置,那么就可以使用 oneof 修饰,也能节约内存
相应格式为:

oneof 字段名 {
	字段1 = 编号;
	字段2 = 编号;
	....
}

注意:oneof 内的 编号不能和消息中的已有编号重复

比如,我们新增联系人信息一个 “其他联系方式”,从QQ号和微信号二选一

syntax = "proto3";
package contacts;

import "goole/protobuf/any.proto"

//地址
message Address
{
    string host_address = 1;    //家庭地址
    string unit_address = 2;    //单位地址
}

message PeopleInfo{
	string name = 1;    //姓名
	int32 age = 2;      //年龄

	message Phone{
        string number = 1;
        enum PhoneType{
        	MP = 0; //移动电话
        	TEL = 1; //固定电话	
        }
        PhoneType type = 2; //类型
    }
    repeated Phone phone = 3;   //电话  
    google.protobuf.Any data = 4;
	//其他联系方式,多选一
	oneof other_contact {
		string qq = 5;
		string wechat = 6;
	}
}

message Contacts{   
    repeated PeopleInfo peoples = 1;    //多个联系人
}

经编译后:
oneof 的多个字段会被定义为一个枚举类型
设置和获取同上,但注意只能设置一个,如果设置多个,最终结果只保留最后一次设置的信息
清空 oneof 字段:clear_ 方法
获取当前设置了哪个字段:_case 方法


write.cc 新增代码

	//其他联系方式
    std::cout << "请选择其他联系方式(1.QQ  2.wechat): ";
    int other_contact;
    std::cin >> other_contact;
    std::cin.ignore(256, '\n');
    if(other_contact == 1)
    {
        std::cout << "请输入QQ号:";
        std::string qq;
        getline(std::cin, qq);
        people->set_qq(qq);
    } 
    else if(other_contact == 2)
    {
        std::cout << "请输入微信号: ";
        std::string wechat;
        getline(std::cin, wechat);
        people->set_wechat(wechat);
    }
    else
        std::cout << "输入有误,无其他联系方式" << std::endl;

注意:虽然 oneof 可以重复设置,但仍保持唯一性,最终结果为最后一次设置的类型

read.cc

//其他联系方式
//读取方式一
if(people.has_qq())
    std::cout << "qq号: " << people.qq() << std::endl;
else if(people.has_wechat())
    std::cout << "微信号: " << people.wechat() << std::endl;
    
//读取方式二
switch (people.other_contact_case())
{
case contacts2::PeopleInfo::OtherContactCase::kQq:
    std::cout << "QQ号: " << people.qq() << std::endl;
    break;
case contacts2::PeopleInfo::OtherContactCase::kWechat:
    std::cout << "微信号: " << people.wechat() << std::endl;
    break;
default:
    break;
}

map

map也就是哈希表,支持创建关联映射字段,格式为:

map<key_type, value_type> map_field = 字段编号;

注意:

  • key_type:除 float 和 bytes 类型以外的任意标量类型。value_type:可以是任意类型
    map 字段不可以用 repeated 修饰
    map 中存入的元素是无序的

新增备注字段,有备注标题和备注信息两个部分

syntax = "proto3";
package contacts;

import "goole/protobuf/any.proto"

//地址
message Address
{
    string host_address = 1;    //家庭地址
    string unit_address = 2;    //单位地址
}

message PeopleInfo{
	string name = 1;    //姓名
	int32 age = 2;      //年龄

	message Phone{
        string number = 1;
        enum PhoneType{
        	MP = 0; //移动电话
        	TEL = 1; //固定电话	
        }
        PhoneType type = 2; //类型
    }
    repeated Phone phone = 3;   //电话  
    google.protobuf.Any data = 4;
	//其他联系方式,多选一
	oneof other_contact {
		string qq = 5;
		string wechat = 6;
	}
	//备注
	map<string, string> remark = 7;
}

message Contacts{   
    repeated PeopleInfo peoples = 1;    //多个联系人
}

对于Map类型字段

  • 清空map:clear_ 方法
  • 设置和获取:获取方法的方法名称和小写字段名称相同。设置方法为 mutable_ 方法,返回值为 Map 类型的指针,已经开辟好空间,直接对这块空间的内容进行修改即可
  • map的遍历可以采用迭代器访问,cbegin()返回起始元素迭代器,cend()返回终止元素迭代器,遍历使用++

write.cc:设置数据

//备注信息
for(int i = 0; ;++i)
{
    std::cout << "请输入第" << i + 1 << "备注信息标题(仅回车表示结束): ";
    std::string remark_info;
    getline(std::cin, remark_info);
    if(remark_info.empty())
        break;
    std::cout << "请输入备注信息内容: ";
    std::string remark_message;
    getline(std::cin, remark_message);

    people->mutable_remark()->insert({remark_info, remark_message});
}

read.cc:迭代器获取数据

//备注信息
for(auto it = people.remark().cbegin(); it != people.remark().cend(); ++it)
    std::cout << "备注信息标题: " << it->first << std::endl 
    << "备注信息内容: " << it->second << std::endl;

以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

标签:ProtoBuf,PeopleInfo,contacts,phone,语法,Phone,详解,message,string
From: https://blog.csdn.net/m0_72563041/article/details/142962722

相关文章

  • compareTo()方法详解
    compareTo() 方法是Java中用于比较两个对象的方法,通常用于实现自然排序(naturalordering)。这个方法定义在 Comparable 接口中,因此任何希望使用 compareTo() 方法的类都必须实现这个接口。以下是 compareTo() 方法的一些关键点和用法示例:关键点接口定义:compareTo()......
  • Markdown 基础语法
    Markdown基础语法Markdown是一种简单、易用的标记语言,它几乎可以格式化任何文本。标题使用标题符号#可以创建1~6级标题。例如:#标题等级1...######标题等级6注意:标题符号#与标题文本之间有一个空格。也可以使用=和-创建1、2级标题。例如:标题等级1......
  • Markdown 扩展语法
    Markdown扩展语法注意:在使用这些扩展语法前,请检查您的应用程序是否支持。并非所有的Markdown编辑器都支持下面的这些扩展语法。表格要创建表格,请使用连字符-创建表头,使用管道符|分隔每列。例如:|学号|姓名|性别|年龄|学分||---|---|---|---|---||001|张三|男|21|87......
  • Softmax函数计算详解
    Softmax函数计算详解Softmax函数的组成部分:输入示例输出概率分布参考Softmax函数的组成部分:σ(z⃗......
  • 十四、Python基础语法(字符串str-下)
    一、字符串替换-replace语法:字符串.replace(old_str,new_str,count)将字符串中的old_str替换为new_str,count为替换的总次数,如果不写,表示全部替换,替换后返回一个完整的字符串,原来的不会改变。count为1,表示替换第一个值。my_str='testtestpython'#全部替换print......
  • 十二、Python基础语法(字符串str-上)
    一、定义字符串:使用单引号、双引号、三引号引用起的内容就是字符串。如果字符串本身包含引号时,要使用其他引号,也可以使用\转义,如果不想字符串中的字符进行转义,可以在字符串前面加上r。name1='python'name2="python"name3="""python"""name4='''python'''#如果......
  • 【信奥赛·C++基础语法】CSP-J C++ 指针与引用
    序言指针和引用是非常重要的概念,它们提供了对内存的直接访问和操作方式,使得程序员能够更加灵活地处理数据哈,理解指针和引用的工作原理以及正确使用它们,对于编写高效、安全的C++程序至关重要。一、指针的基本概念指针的定义和作用指针是一个变量,它存储了另一个变量的内......
  • static 和 extern详解
    目录1.static与extern的简单介绍2static修饰局部变量:3.static修饰全局变量4.static修饰函数1.static与extern的简单介绍static和extern都是C语⾔中的关键字。static是静态的的意思,可以⽤来:•修饰局部变量•修饰全局变量•修饰函数extern是⽤来......
  • Java工程师必备的20条SQL最佳实践详解
    在Java开发中,SQL是处理数据库交互的核心技能。高效的SQL查询不仅能够提升应用程序的性能,还能减少资源消耗和提高用户体验。以下是Java工程师必备的20条SQL最佳实践,每条都附有代码示例和详细解释。1.使用索引索引可以显著提高查询速度。为经常用于查询条件、排序和连接的......
  • 如何在 Spring Boot 中处理 Protobuf 数据格式
    个人名片......