Protocol Buffers V3语法全解

news2024/11/26 21:25:51

目录

  • protobuf介绍
  • protobuf使用
  • protoc命令
  • 语法
    • 定义消息类型
      • 指定字段类型
      • 分配字段编号
      • 指定字段规则
      • 添加更多消息类型
      • 注释
      • 保留字段
      • 从.proto文件生成了什么?
    • 值类型
    • 默认值
    • 枚举
    • 使用其他消息类型
      • 导入定义
    • 嵌套类型
    • 更新消息类型
    • 未知字段
    • any任意类型
    • oneof
      • oneof 特性
      • 兼容性问题
    • Maps
    • Packages
    • 定义服务
    • JSON 映射
    • Options
  • 参考文档

protobuf介绍

Protobuf是Protocol Buffer的简称,它是Google公司于2008年开源的一种高效的平台无关、语言无关、可扩展的数据格式,目前Protobuf作为接口规范的描述语言,可以作为Go语言RPC接口的基础工具。

protobuf使用

protobuf是一个与语言无关的一个数据协议,所以我们需要先编写IDL文件然后借助专用工具生成指定语言的代码,从而实现数据的序列化与反序列化过程。

大致开发流程如下: 1. IDL编写 2. 生成指定语言的代码 3. 序列化和反序列化

protoc命令

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
  1. IMPORT_PATH指定寻找proto 的目录去解决import 带来的依赖问题,如果省略,默认是当前文件夹。多个import 文件夹可以通过参数使用–proto_path多次来解决,编译器将会按顺序搜索。也可以用简写-I=IMPORT_PATH来表示–proto_path
  2. –cpp_out,–java_out,–go_out等等代表指定生成的语言,可以生成多个语言
  3. path/to/file.proto 代表输入的proto 文件,可以用*.proto 代表输入文件夹内多个文件

语法

定义消息类型

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • 文件第一行指定您使用的语法:如果不这样做,编译器将假定使用的是proto2。
  • 这必须是.proto3文件的第一个非空、非注释行。
  • 消息定义指定了三个字段(名称/值对),每个字段对应于要包含在该类型消息中的数据段。

指定字段类型

在上面的示例中,所有字段都是标量类型(scalar types): 两个整数(page_number和 result_per_page)和一个字符串(query)。但是也可以为字段指定组合类型,包括枚举和其他消息类型。

在这里插入图片描述

分配字段编号

  1. 消息定义中的每个字段都有一个唯一编号。
  2. 这些字段编号用于标识消息二进制格式的字段,并且在消息类型投入使用后不应更改。
  3. 请注意,1到15范围内的字段编号需要一个字节进行编码,编码内包括字段号和字段类型(参考协议缓冲区编码)。
  4. 16到2047范围内的字段编号需要两个字节(进行编码)。因此,您应该把1到15的消息编号留给非常频繁出现的消息元素。
  5. 请记住为将来可能添加的频繁出现的元素留出一些空间。可以指定的最小字段号为1,最大字段号为(2**29)-1(字段数字会作为key,key最后三位是类型)或536870911。您也不能使用数字1900019999(字段描述符),因为它们是协议缓冲区的保留数字,如果你在你的.proto中使用了这些数字,编译器会报错。同样,不能使用任何以前保留的字段号。

指定字段规则

消息字段可以是下列字段之一:

  1. singular: 格式正确的消息可以有这个字段的零个或一个(但不能多于一个)。这是 proto3语法的默认字段规则。
  2. repeated: 该字段可以在格式正确的消息中重复任意次数(包括零次)。重复值的顺序将被保留。

在 proto3中,标量数值类型的repeated字段默认使用packed编码。(参考Protocol Buffer Encoding,可以找到关于packed编码的更多信息)

添加更多消息类型

  1. 可以在一个.proto 文件中定义多个消息类型。
  2. 如果想定义与 SearchRequest 消息类型对应的应答消息格式SearchResponse,就可以将其添加到同一个.proto文件中。
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
 ...
}

注释

.proto文件使用了C/C++风格的///* ... */语法。

/* SearchRequest 表示一个分页查询 
 * 其中有一些字段指示响应中包含哪些结果 */

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // 页码数
  int32 result_per_page = 3;  // 每页返回的结果数
}

保留字段

  1. 如果通过完全删除某个字段或把它注释掉来更新消息类型,则将来的用户可以在对该类型进行自己的更新时重用该字段编号。
  2. 如果以后加载相同.proto的旧版本,这可能会导致数据损坏、隐私漏洞等严重问题。
  3. 确保不会发生这种情况的一种方法是使用reserved关键字指定已删除字段的字段编号为保留编号(也要指定已删除字段名称为保留名称(name),以规避JSON序列化问题)。将来有任何用户试图使用这些字段标识符时,协议缓冲区编译器将报错。
message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

注意,不能在同一个reserved语句中混合字段名和字段编号。

从.proto文件生成了什么?

当使用 protocol buffer 编译器来运行.proto文件时,编译器用选择的语言生成描述的消息类型,包括获取和设置字段值,将消息序列化为输出流,以及从输入流解析消息的代码。

  1. 对于Python:Python 编译器为.proto文件中的每个消息类型生成一个带静态描述符的模块,然后与 metaclass 一起使用,在运行时创建必要的 Python 数据访问类。

  2. 对于 Go:编译器为文件中的每种消息类型生成一个类型(type)到一个.pb.go 文件。

  3. 其他…

值类型

.proto TypeNotesC++ TypeJava/Kotlin Type[1]Python Type[3]Go TypePHP Type
doubledoubledoublefloatfloat64float
floatfloatfloatfloatfloat32float
int32使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,则使用 sint32代替。int32intintint32integer
int64使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,则使用 sint64代替。int64longint/long[4]int64integer/string[6]
uint32使用变长编码。uint32int[2]int/long[4]uint32integer
uint64使用变长编码。uint64long[2]int/long[4]uint64integer/string[6]
sint32使用可变长度编码。带符号的 int 值。这些编码比普通的 int32更有效地编码负数。int32intintint32integer
sint64使用可变长度编码。带符号的 int 值。这些编码比普通的 int64更有效地编码负数。int64longint/long[4]int64integer/string[6]
fixed32总是四个字节。如果值经常大于228,则比 uint32更有效率。uint32int[2]int/long[4]uint32integer
fixed64总是8字节。如果值经常大于256,则比 uint64更有效率。uint64integer/string[6]
sfixed32总是四个字节。int32intintint32integer
sfixed64总是八个字节。int64integer/string[6]
boolboolbooleanboolboolboolean
string字符串必须始终包含 UTF-8编码的或7位 ASCII 文本,且不能长于232。stringStringstr/unicode[5]stringstring
bytes可以包含任何不超过232字节的任意字节序列。stringByteStringstr (Python 2) bytes (Python 3)[]bytestring

默认值

当解析消息时,如果编码消息不包含特定的 singular 元素,则解析对象中的相应字段将设置为该字段的默认值。

  1. string:默认值为空字符串
  2. bytes:默认值为空字节
  3. boolean:默认值为false
  4. 数值类型:默认值为0
  5. 枚举:默认值为第一个定义的枚举值,该值必须是0
  6. 消息字段:不设默认值,它的确切值取决于语言。
  7. repeated 字段的默认值为空(通常是适配语言中的空列表)。

枚举

定义消息类型时,可能希望其中一个字段只包含预定义值列表中的一个

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

Corpus enum 的第一个常量映射为零,每个 enum 定义必须包含一个常量,该常量映射为零且作为它的第一个元素。这是因为:

  1. 必须有一个零值,这样我们就可以使用0作为数值默认值。
  2. 零值必须是第一个元素,以便与 proto2语义兼容,其中第一个枚举值总是默认值。

可以通过将相同的值赋给不同的枚举常量来定义别名。为此,需要将allow_alias选项设置为true,否则协议编译器将在找到别名时将生成错误消息。

message MyMessage1 {
  enum EnumAllowingAlias {
  	// 允许别名
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
  }
}
message MyMessage2 {
  enum EnumNotAllowingAlias {
    UNKNOWN = 0;
    STARTED = 1;
    // RUNNING = 1;  // 取消对此行的注释将导致内部出现编译错误,外部出现警告消息。
  }
}
  1. 枚举器常量必须在32位整数的范围内。由于enum使用可变编码,因此负值效率很低,所以不建议使用。您可以在定义的消息内部定义枚举,如上面的示例所示,也可以在外部定义枚举——这些枚举可以在.proto文件中的任何消息定义中重用。您还可以使用_MessageType_._EnumType_语法将一条消息中声明的枚举类型用作另一条消息中的字段类型。
  2. 当使用协议缓冲区编译器编译一个使用了枚举的.proto文件时,对于Python,会生成一个特殊EnumDescriptor类,用于在运行时生成的类中创建一组具有整数值的符号常量。
syntax = "proto3";
option go_package = "protos/pbs";
enum TestType {
  Hello1=0;
  Hello2=1;
  Hello3=2;
  Hello4=3;
}

message HelloRequest {
  string greeting = 1;
  TestType en=2;
}

message HelloResponse {
  string reply = 1;
}

使用其他消息类型

当使用其他消息的时候,如果在本文件,直接使用就可以了。Result代表自定义消息类型

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  // []string
  repeated string snippets = 3;
}

导入定义

可以通过 import 来使用来自其他 .proto 文件的定义。

import "myproject/other_protos.proto";

默认情况下,只能从直接导入的 .proto 文件中使用定义。但是,有时你可能需要将 .proto 文件移动到新的位置,这样就可以在旧目录放一个占位的.proto文件,使用import public 将所有导入转发到新位置,而不必直接移动.proto文件并修改所有的地方。

任何导入包含import public语句的proto的人都可以传递地依赖import public依赖项

// new.proto
// All definitions are moved here
-----------------------------------------------
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
-----------------------------------------------
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto

protocol 编译器使用命令行-I/–proto_path参数指定的一组目录中搜索导入的文件。如果没有给该命令行参数,则查看调用编译器的目录。

一般来说,应该将 --proto_path 参数设置为项目的根目录并为所有导入使用正确的名称。

// user_business.proto
syntax = "proto3";
option go_package = "protos/pbs";
import "share/user.proto";
//获取角色信息请求
message GetUserRequest {

}
//获取角色信息响应
message GetUserResponse {
  User user=1;
}
// user.proto
syntax = "proto3";
option go_package = "protos/pbs";

//用户定义
message User {
	string Id=1;
	string Name=2;
	string Age=3;
}

嵌套类型

可以在其他消息类型中定义和使用消息类型,在 SearchResponse消息中定义Result:

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

如果要在其父消息类型之外重用此消息类型,请通过_Parent_._Type_使用:

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}

可以随心所欲地将信息一层又一层嵌入其中:

message Outer {                  // Level 0
  message MiddleAA {  // Level 1
    message Inner {   // Level 2
      int64 ival = 1;
      bool  booly = 2;
    }
  }
  message MiddleBB {  // Level 1
    message Inner {   // Level 2
      int32 ival = 1;
      bool  booly = 2;
    }
  }
}

更新消息类型

如果现有的消息类型不再满足你的所有需要(例如,消息格式需要增加一个字段),但是仍然希望使用用旧格式创建的代码。在不破坏任何现有代码的情况下更新消息类型非常简单,只需遵守以下规则:

  1. 不要更改任何现有字段的字段编号
  2. 如果添加新字段,则使用“旧”消息格式的代码序列化的任何消息仍然可以由新生成的代码进行解析。你应该记住这些元素的默认值,以便新代码可以正确地与旧代码生成的消息交互。类似地,由新代码创建的消息也可以由旧代码解析:旧二进制文件在解析时忽略新字段。
  3. 可以删除字段,前提是在更新的消息类型中不再使用此字段编号。您可能需要重命名字段,或者添加前缀OBSOLETE_,或者使用reserved保留字段编号,以便.proto的未来用户不会意外地重用该编号。
  4. int32uint32int64uint64bool都是兼容的——这意味着你可以在不破坏向前或向后兼容性的情况下将一个字段从这些类型中的一个更改为另一个。
  5. 如果一个数字被解析到一个并不适当的类型中,你会得到与在 C++ 中将数字转换为该类型相同的效果(例如,如果一个64位的数字被读作 int32,它将被截断为32位)
  6. sint32sint64相互兼容,但与其他整数类型不兼容。
  7. stringbytes是兼容的,只要字节是有效的 UTF-8。
  8. 如果bytes包含消息的编码版本,则嵌入的消息与字节兼容。
  9. fixed32sfixed32兼容 fixed64sfixed64兼容。
  10. 对于stringbytes和消息字段,optional字段与repeated字段兼容。给定重复字段的序列化数据作为输入,如果该字段是基元类型字段,则期望该字段为optional(可选的)字段的客户端将获取最后一个输入值;如果该字段是消息类型字段,则合并所有输入元素。请注意,对于数值类型(包括boolenum),这通常是不安全的。数字类型的重复字段可以按压缩格式序列化,当需要可选字段时,将无法正确解析压缩格式。
  11. enum 在格式方面与 int32uint32int64uint64兼容(请注意,如果不适合,值将被截断)。但是要注意,当消息被反序列化时,客户端代码可能会对它们进行不同的处理:例如,未识别的proto3枚举类型将保留在消息中,但在反序列化消息时如何表示这些类型取决于客户端语言。整型字段总是保持它们的值。
  12. 将单个值更改为新的oneof成员是安全的,并且二进制兼容。如果确保没有代码一次设置多个字段,那么将多个字段移动到新的oneof字段中可能是安全的。将任何字段移到现有oneof字段中都不安全。

未知字段

  1. 未知字段是格式良好的协议缓冲区序列化数据,表示解析器无法识别的字段。例如,当一个旧二进制代码解析一个带有新字段的新二进制代码发送的数据时,这些新字段在旧二进制代码中成为未知字段。
  2. 最初,proto3消息在解析过程中总是丢弃未知字段,但在3.5版中,重新引入了未知字段的保留,以匹配proto2的行为。
  3. 在版本3.5和更高版本中,解析期间保留未知字段,并将其包含在序列化输出中。

any任意类型

  1. Any 消息类型允许你将消息作为嵌入类型使用,而不需要其 .proto 定义。
  2. Any包含一个以字节表示的任意序列化消息,以及一个URL,该URL充当该消息的全局唯一标识符并解析为该消息的类型。
  3. 要使用 Any类型,需要导入google/protobuf/any.proto。
  4. 如果编译报错,将上述文件下载好复制到自己的proto 目录再编译。
import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  repeated google.protobuf.Any details = 2;
}

给定消息类型的默认类型 URL 是type.googleapis.com/_packagename_._messagename_

不同的语言实现将支持运行库助手以类型安全的方式打包和解包 Any值。例如在java中,Any类型会有特殊的pack()和unpack()访问器,在C++中会有PackFrom()和UnpackTo()方法。

oneof

如果在平时在一个消息有许多字段,但是最多设置一个字段,我们可以使用oneof 来执行并节省内存。

Oneof 字段类似于常规字段,除了Oneof共享内存的所有字段之外,最多可以同时设置一个字段。设置Oneof 的任何成员都会自动清除所有其他成员。您可以使用case()或WhichOneof()方法检查Oneof 中的哪个值被设置(如果有的话),具体取决于选择的语言。

syntax = "proto3";
option go_package = "protos/pbs";

message SubMessage {
  int32 Id=1;
  string Age2=2;

}
message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

要定义 oneof 字段需要在你的.proto文件中使用oneof关键字并在后面跟上名称,然后将其中一个字段添加到该字段的定义中。你可以添加任何类型的字段,除了map字段和repeated字段。

oneof 特性

  1. 设置一个字段将自动清除该字段的所有其他成员。如果设置了多个 oneof字段,那么只保留最后的一个值。
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message();   // Will clear name field.
CHECK(!message.has_name());
  1. oneof 不支持repeated。
  2. 反射作用于oneof的字段。
package main

import (
	"fmt"
	"grpcdemo/protobuf/any/protos/pbs"
)

func main()  {

	p:=&pbs.SampleMessage{
		TestOneof: &pbs.SampleMessage_Name{Name: "hello"},
	}

	fmt.Println(p)
	fmt.Println(p.GetTestOneof())
	p.TestOneof=&pbs.SampleMessage_SubMessage{SubMessage: &pbs.SubMessage{Id: 1}}
	fmt.Println(p)
	fmt.Println(p.GetTestOneof())
}

兼容性问题

添加或删除其中一个字段时要小心。如果检查 oneof 的值返回 None/NOT_SET,则可能意味着 oneof 尚未设置或已设置为 oneof 的另一个字段。这种情况是无法区分的,因为无法知道未知字段是否是 oneof 成员。

标签重用问题

  1. 将 optional 可选字段移入或移出 oneof:在序列化和解析 message 后,你可能会丢失一些信息(某些字段将被清除)。但是,你可以安全地将单个字段移动到新的 oneof 中,并且如果已知只有一个字段被设置,则可以移动多个字段。
  2. 删除 oneof 字段并将其重新添加回去:在序列化和解析 message 后,这可能会清除当前设置的 oneof 字段。
  3. 拆分或合并 oneof:这与移动常规的 optional 字段有类似的问题。

Maps

map<key_type, value_type> map_field = N;


message Project {
  key_type key = 1;
  value_type value = 2;
}

// 比如key是string val是Project类型
map<string, Project> projects = 3;

其中key_type可以是任何整型或字符串类型(因此,除了浮点类型和字节之外的任何标量类型)。注意enum不是一个有效的key_typevalue_type可以是除其他map以外的任何类型。

  1. 映射字段不能使用repeated关键字。
  2. 映射值的Wire格式排序和映射迭代排序未定义,因此不能依赖特定顺序的映射项。
    为.proto生成文本格式时,映射按键排序。数字键按数字排序。
  3. 如果为映射字段提供键但没有值,则序列化字段时的行为与语言有关。在C++、Java和Python中,类型的默认值被序列化,而在其他语言中没有任何序列化。
  4. 生成的map API目前适用于所有proto3支持的语言。

Packages

可以向.proto文件中添加可选的package明符,以防止协议消息类型之间的名称冲突,
可以理解为go的包。

package foo.bar;
message Open { ... }

定义服务

如果要在RPC(Remote Procedure Call,远程过程调用)系统中使用消息类型,可以在.proto文件中定义RPC服务接口。

service SearchService {
  rpc Search(SearchRequest) returns (SearchResponse);
}

JSON 映射

proto3JSONJSON example描述【译】
messageobject{"fooBar": v, "g": null, …}生成JSON对象。消息字段名被映射到lowerCamelCase并成为JSON对象键。如果指定了json_name选项,则指定的值将用作键。解析器接受小驼峰命秘法名称(或由json_name选项指定的名称)和原始proto字段名称。null是所有字段类型的可接受值,并被视为相应字段类型的默认值。
enumstring"FOO_BAR"使用proto中指定的枚举值的名称。解析器接受枚举名和整数值。
map<K,V>object{"k": v, …}所有键都转换为字符串。
repeated Varray[v, …]null被接受为空列表[]
booltrue, falsetrue, false
stringstring"Hello World!"
bytesbase64 string"YWJjMTIzIT8kKiYoKSctPUB+"JSON值将是使用带填充的标准base64编码的字符串编码的数据。包含或不包含填充的标准或url安全base64编码都可以接受。
int32, fixed32, uint32number1, -10, 0JSON值将是一个十进制数。可接受数字或字符串。
int64, fixed64, uint64string"1", "-10"JSON值将是十进制字符串。可接受数字或字符串。
float, doublenumber1.1, -10.0, 0, "NaN", "Infinity"JSON值将是一个数字或特殊字符串值"NaN", "Infinity",和"-Infinity"中的一个。数字或字符串都可以接受。指数符号也被接受。-0被认为等于0。
Anyobject{"@type": "url", "f": v, … }如果`Any`类型包含一个具有特殊JSON映射的值,它将按如下方式转换:{"@type":xxx,"value":yyy}。否则,该值将转换为JSON对象,并插入“@type”字段以指示实际的数据类型。
Timestampstring"1972-01-01T10:00:20.021Z"使用RFC3339,其中生成的输出将始终是**Z**规格化的,并使用0、3、6或9个小数位数。也接受“Z”以外的偏移。
Durationstring"1.000340012s", "1s"生成的输出总是包含0、3、6或9个小数位数(取决于所需的精度),后跟“s”后缀。接受任何小数位数(没有小数也可以),只要它们符合纳秒精度,并且需要“s”后缀。
Structobject{ … }任何JSON对象。见struct.proto
Wrapper typesvarious types2, "2", "foo", true, "true", null, 0,包装器在JSON中使用与包装原语类型相同的表示形式,只是在数据转换和传输期间允许并保留null
FieldMaskstring"f.fooBar,h"field_mask.proto
ListValuearray[foo, bar, …]
Valuevalue任何JSON值。详见google.protobuf.Value
NullValuenullJSON null
Emptyobject{}空的JSON对象

Options

  1. .proto文件中的单个声明可以使用许多 选项 进行注释。选项不会更改声明的总体含义,但可能会影响在特定上下文中处理声明的方式。可用选项的完整列表在google/protobuf/descriptor.proto中定义。

  2. deprecated选项: 设为true 代表字段被废弃,在新代码不应该被使用,在大多数语言都没有实际的效果

option go_package = "tmp/pb";  // 指定生成的Go代码在你项目中的导入路径

int32 old_field = 6 [deprecated = true];

参考文档

[1] 深入解析protobuf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/336212.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

awk命令

一.介绍 awk是专门为文本处理设计的编程语言&#xff0c;是一门数据驱动的编程语言。与sed类似&#xff0c;都是以数据驱动的行处理软件&#xff0c;主要用于数据扫描&#xff0c;过滤和汇总。数据可以来自于标准输入&#xff0c;管道或者文件。 二.语法 awk是一种处理文本文件…

Vite+Vue3实现版本更新检查,实现页面自动刷新

ViteVue3实现版本更新检查&#xff0c;实现页面自动刷新1、使用Vite插件打包自动生成版本信息2、Vite.config.ts配置3、配置环境变量4、路由配置现有一个需求就是实现管理系统的版本发版&#xff0c;网页实现自动刷新页面获取最新版本 搜索了一下&#xff0c;轮询的方案有点浪费…

Mysql:避免重复的插入数据方法汇总

最常见的方式就是为字段设置主键或唯一索引&#xff0c;当插入重复数据时&#xff0c;抛出错误&#xff0c;程序终止&#xff0c;但这会给后续处理带来麻烦&#xff0c;因此需要对插入语句做特殊处理&#xff0c;尽量避开或忽略异常&#xff0c;下面我介绍4种方法&#xff1a; …

【Windows10】电脑副屏无法调节屏幕亮度?解决方法

先说下情况&#xff0c;本人对显示器不太懂&#xff0c;属于小白 这个副屏无法调节的问题出现也已经很久了&#xff0c;但是之前亮度适合就无所谓&#xff0c;今天突然按了之后很亮&#xff0c;于是就找问题。 第一步&#xff0c;我直接百度&#xff0c;遇事不决&#xff0c;百…

59 双向循环神经网络【动手学深度学习v2】

59 双向循环神经网络【动手学深度学习v2】 深度学习学习笔记 学习视频&#xff1a;https://www.bilibili.com/video/BV12X4y1c71W/?spm_id_fromautoNext&vd_source75dce036dc8244310435eaf03de4e330 核心思想&#xff1a;取决于过去和未来的上下文&#xff0c;来预测当前的…

Apache Spark 命令注入(CVE-2022-33891)

利用范围 Spark Core - Apache <3.0.3 3.1.1 < Spark Core - Apache <3.1.2 3.2.0 < Spark Core - Apache <3.2.1 环境搭建 修改bin目录下的spark-shell 修改如下&#xff0c;添加调试端口 变动如上 然后启动 ./spark-shell --conf spark.acls.enabletrue即…

使用百度地图官方WEB API,提示 “ APP 服务被禁用“ 问题的解决方法

问题描述 项目上用了百度地图官方WEB API&#xff0c;打开界面时百度地图无法打开&#xff0c;出现弹窗&#xff1a; APP被您禁用啦。详情查看&#xff1a;http://lbsyun.baidu.com/apiconsole/key#。 原因分析&#xff1a; 查看错误信息&#xff1a;"status":240,…

Word文档带有权限密码怎么办?

Word文档的权限密码指的是什么&#xff1f;其实这是Word文档的保护方法之一&#xff0c;具体指Word文档的编辑、修改受到了限制&#xff0c;需要输入密码才能进行。 设置了权限密码的Word文档还是可以直接打开&#xff0c;只有当需要编辑或者修改内容的时候&#xff0c;才会发…

CSDN版的详细MarkDown的使用教程

MarkDown的使用欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释…

雅思经验(7)

我发现雅思阅读要命的不是难度&#xff0c;而是时间的把控。考试时间是总共一小时&#xff0c;但是要写三篇文章&#xff0c;之后总共40道题目&#xff0c;也就是说每篇文章平均是13.3道。但是他们很多人说&#xff0c;如果誊写答案需要花掉3、4分钟每篇&#xff0c;也就是说真…

学习IB课程的宝藏网站,有需求选择

很多学习IB课程的同学都会觉得IB课程很难&#xff0c;平时作业很多根本没时间找资料&#xff0c;其实不用担心&#xff0c;关于IB课程学习的网站&#xff0c;小编已经帮大家整理好了&#xff0c;建议收藏起来&#xff01; 数学类IB网站&#xff1a;Revision Villag 数学对于会的…

Bard AI:训练过程中使用了多少数据?

近年来&#xff0c;人工智能取得了长足的进步&#xff0c;并在科技界掀起了波澜。 随着谷歌最近推出新的人工智能聊天机器人 Bard&#xff0c;人们对这项技术的工作原理以及训练它的内容感到好奇。 人工智能技术的关键组成部分之一是训练过程中使用的数据量&#xff0c;这有助于…

聊聊数据仓库是什么

随着数据通过各种方式创造了巨大价值&#xff0c;各领域的企业开始不断挖掘数据的作用&#xff0c;数据的重要性得到了社会各界的共同认可。像我们熟知的数据治理、数据管理、数据标准以及数据资产都是因为数据地位不断提升&#xff0c;企业开始重视起数据全生命周期流程&#…

整数规划、对偶理论、线性规划经典例题讲解

整数规划是一类要求问题的解中的全部或一部分变量为整数的数学规划&#xff0c;应用范围极其广泛。不仅在工业和工程设计和科学研究方面有许多应用&#xff0c;而且在计算机设计、系统可靠性和经济分析等方面也有新的应用。通过前面的学习&#xff0c;我们已经掌握了整数规划的…

Java测试——selenium具体操作

selenium的前置准备工作可以参考我之前的博客&#xff1a;Java测试——selenium的安装与使用教程 这篇博客讲解一下selenium的常见操作 先创建driver ChromeDriver driver new ChromeDriver();输入网址 driver.get("https://www.baidu.com");常见操作 查找元素…

[数据分析] 数据指标体系搭建

在数据分析的学习过程中&#xff0c;我们通常会要求掌握以下两点: 1.理解数据&#xff0c;懂得从数据中发现业务指标(学会如何去看懂数据) 2.使用相关指标去分析数据&#xff0c;同时使用多个指标去分析一个问题(了解常见的指标) 当我们拿到数据(通常以Excel或者数据库方式去…

机器学习评估指标的十个常见面试问题

评估指标是用于评估机器学习模型性能的定量指标。它们提供了一种系统和客观的方法来比较不同的模型并衡量它们在解决特定问题方面的成功程度。通过比较不同模型的结果并评估其性能可以对使用哪些模型、如何改进现有模型以及如何优化给定任务的性能做出正确的决定&#xff0c;所…

java世界String的那些事

String的创建机理&#xff1a; 由于String在Java世界中使用过于频繁&#xff0c;Java为了避免在一个系统中产生大量的String对象&#xff0c;引入了字符串常量池。其运行机制是&#xff1a;创建一个字符串时&#xff0c;首先检查池中是否有值相同的字符串对象&#xff0c;如果…

【Nacos】Nacos配置中心客户端配置更新源码分析

上文我们说了服务启动的时候从远程Nacos服务端拉取配置&#xff0c;这节我们来说下Nacos服务端配置的变动怎么实时通知到客户端&#xff0c;首先需要注册监听器。 注册监听器 NacosContextRefresher类会监听应用启动发布的ApplicationReadyEvent事件&#xff0c;然后进行配置…

现在00后也这么卷?部门刚来的00后软件测试工程师已经要把我卷崩溃了...

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;刚开年我们公司来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最…