Table of Contents

Protocol Buffers

定义消息类型

指定字段类型

分配字段编号

指定字段规则

添加更多消息类型

添加评论

保留字段

您产生了什么.proto?

标量值类型

可选字段和默认值

枚举

保留值

使用其他消息类型

导入定义

使用proto3消息类型

嵌套类型

团体

更新消息类型

扩展名

嵌套扩展

选择分机号码

编码方式

一个简单的消息

基础128种

讲解

协议缓冲区基础:C ++

为什么要使用协议缓冲区?

在哪里可以找到示例代码

定义协议格式

Protocol Buffer使用简介

1.概览

1.1 什么是protocol buffer

2.使用

2.1定义一个消息类型

字段限制

Tags

注释

2.2 值类型

2.3 可选字段与缺省值

2.4 枚举

2.5 使用其他消息类型

2.6 嵌套类型

2.7 更新一个数据类型

2.8 扩展

2.9 选项

3.常用API介绍

3.1 基本函数

3.2 消息实现

3.3 嵌套类型

3.4 访问器

3.5 其他函数

3.6 使用例子


 

我们项目中使用protocol buffer来进行服务器和客户端的消息交互,服务器使用C++,所以本文主要描述protocol buffer C++方面的使用,其他语言方面的使用参见google的官方文档.protocol buffer是google的一个开源项目,它是用于结构化数据串行化的灵活、高效、自动的方法,例如XML,不过它比xml更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。

 

Protocol Buffers

https://developers.google.cn/protocol-buffers/docs/proto?hl=zh-cn


本指南介绍了如何使用协议缓冲区语言来构造协议缓冲区数据,包括.proto文件语法以及如何从.proto文件中生成数据访问类。它涵盖了协议缓冲区语言的proto2版本:有关proto3语法的信息,请参见《Proto3语言指南》。

这是参考指南–有关使用本文档中描述的许多功能的分步示例,请参阅所选语言的教程。

定义消息类型

首先,让我们看一个非常简单的示例。假设您要定义一个搜索请求消息格式,其中每个搜索请求都有一个查询字符串,您感兴趣的特定结果页面以及每页结果数量。这是.proto用于定义消息类型的文件。


message SearchRequest {required string query = 1;optional int32 page_number = 2;optional int32 result_per_page = 3;
}

所述SearchRequest消息定义指定了三个字段(名称/值对),一个用于每条数据要在此类型的消息包括。每个字段都有一个名称和类型。

指定字段类型

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

分配字段编号

如您所见,消息定义中的每个字段都有一个唯一的编号。这些数字用于标识消息二进制格式的字段,一旦使用了消息类型,就不应更改这些数字。请注意,范围为1到15的字段编号需要一个字节来编码,包括字段编号和字段的类型(您可以在Protocol Buffer Encoding中找到有关此内容的更多信息)。16到2047之间的字段号占用两个字节。因此,应该为非常频繁出现的消息元素保留字段编号1到15。切记为将来可能添加的频繁出现的元素留出一些空间。

您可以指定最小的场数是1,最大为2 29日- 1,或536870911。您也不能使用数字19000到19999(FieldDescriptor::kFirstReservedNumberFieldDescriptor::kLastReservedNumber),因为它们是为协议缓冲区实现保留的-如果您在中使用这些保留数之一,则协议缓冲区编译器会抱怨.proto。同样,您不能使用任何以前保留的字段号。

指定字段规则

您指定消息字段是以下内容之一:

  • required:格式正确的消息必须恰好具有此字段之一。
  • optional:格式正确的邮件可以包含零个或一个此字段(但不能超过一个)。
  • repeated:在格式正确的消息中,此字段可以重复任意次(包括零次)。重复值的顺序将保留。

由于历史原因,repeated标量数字类型的字段编码效率不如预期。新代码应使用特殊选项[packed=true]来获得更有效的编码。例如:

repeated int32 samples = 4 [packed=true];

您可以packed在协议缓冲区编码中找到有关编码的更多信息。

永远是必需的您将字段标记为时应格外小心required。如果您希望停止写入或发送必填字段,则将该字段更改为可选字段会很麻烦–老读者会认为没有该字段的邮件是不完整的,可能会无意中拒绝或丢弃它们。您应该考虑为缓冲区编写特定于应用程序的自定义验证例程。

当有人向枚举值添加时,出现带有必填字段的第二个问题。在这种情况下,无法识别的枚举值将被视为丢失,这也会导致所需的值检查失败。

添加更多消息类型

可以在一个.proto文件中定义多种消息类型。如果要定义多个相关消息,这很有用–例如,如果要定义与您的SearchResponse消息类型相对应的回复消息格式,则可以将其添加到相同的消息中.proto


message SearchRequest {required string query = 1;optional int32 page_number = 2;optional int32 result_per_page = 3;
}message SearchResponse {...
}

合并消息会导致膨胀。虽然可以在单个.proto文件中定义多种消息类型(例如,消息,枚举和服务),但是当在单个文件中定义大量具有不同依赖性的消息时,也会导致依赖性膨胀。建议每个.proto文件包含尽可能少的消息类型。

添加评论

要将注释添加到.proto文件中,请使用C / C ++样式///* ... */语法。


/* SearchRequest represents a search query, with pagination options to* indicate which results to include in the response. */message SearchRequest {required string query = 1;optional int32 page_number = 2;  // Which page number do we want?optional int32 result_per_page = 3;  // Number of results to return per page.
}

保留字段

如果您通过完全删除字段或将其注释掉来更新消息类型,则将来的用户在自己对该类型进行更新时可以重用该字段号。如果他们以后加载相同版本的旧版本,可能会导致严重的问题.proto,包括数据损坏,隐私错误等。确保不会发生这种情况的一种方法是,将已删除字段的字段编号(和/或名称,也可能导致JSON序列化的问题)指定为reserved。如果将来有任何用户尝试使用这些字段标识符,则协议缓冲区编译器会抱怨。

message Foo {reserved 2, 15, 9 to 11;reserved "foo", "bar";
}

请注意,您不能在同reserved一条语句中混用字段名称和字段编号。

您产生了什么.proto

在上运行协议缓冲区编译器时.proto,编译器会以您选择的语言生成代码,您将需要使用文件中描述的消息类型,包括获取和设置字段值,将消息序列化为输出流,并从输入流中解析消息。

  • 对于C ++,编译器从每个生成一个.h.cc文件.proto,并为文件中描述的每种消息类型提供一个类。
  • 对于Java,编译器会生成一个.java文件,其中包含每种消息类型的类以及Builder用于创建消息类实例的特殊类。
  • Python稍有不同-Python编译器会在您的中生成一个模块,其中包含每种消息类型的静态描述符,.proto然后将该模块与元类一起使用,以在运行时创建必要的Python数据访问类。
  • 对于Go,编译器.pb.go将为文件中的每种消息类型生成一个具有相应类型的文件。

您可以按照所选语言的教程,找到有关每种语言使用API​​的更多信息。有关API的更多详细信息,请参阅相关的API参考。

标量值类型

标量消息字段可以具有以下类型之一-该表显示.proto文件中指定的类型,以及自动生成的类中的相应类型:

.proto类型笔记C ++类型Java类型Python类型[2]去类型
 浮动* float64
浮动 浮动浮动浮动* float32
int32使用可变长度编码。负数编码效率低下–如果您的字段可能具有负值,请改用sint32。int32整型整型* int32
int64使用可变长度编码。负数编码效率低下–如果您的字段可能具有负值,请改用sint64。int64int / long [3]* int64
uint32使用可变长度编码。uint32整数[1]int / long [3]* uint32
uint64使用可变长度编码。uint64长[1]int / long [3]* uint64
sint32使用可变长度编码。有符号的int值。与常规int32相比,它们更有效地编码负数。int32整型整型* int32
sint64使用可变长度编码。有符号的int值。与常规int64相比,它们更有效地编码负数。int64int / long [3]* int64
固定的始终为四个字节。如果值通常大于2 28,则比uint32更有效。uint32整数[1]int / long [3]* uint32
固定64始终为八个字节。如果值通常大于2 56,则比uint64更有效。uint64长[1]int / long [3]* uint64
固定32始终为四个字节。int32整型整型* int32
固定的始终为八个字节。int64int / long [3]* int64
布尔 布尔布尔值布尔*布尔
字符串必须始终包含UTF-8编码或7位ASCII文本。unicode(Python 2)或str(Python 3)*串
个字节可以包含任意字节序列。字节串个字节[]字节

当您在Protocol Buffer Encoding中对消息进行序列化时,您可以找到更多有关如何编码这些类型的信息。

[1]在Java中,无符号的32位和64位整数使用带符号的对等体表示,最高位仅存储在符号位中。

[2]在所有情况下,将值设置为字段都会执行类型检查以确保其有效。

[3] 64位或无符号32位整数在解码时始终表示为long,但是如果在设置字段时给出了int,则可以为int。在所有情况下,该值都必须适合设置时表示的类型。参见[2]。

可选字段和默认值

如上所述,消息描述中的元素可以被标记optional。格式正确的消息可能包含也可能不包含可选元素。解析消息时,如果消息中不包含可选元素,则解析对象中的相应字段将设置为该字段的默认值。可以将默认值指定为消息描述的一部分。例如,假设你想提供的10为默认值SearchRequestresult_per_page值。

optional int32 result_per_page = 3 [default = 10];
如果未为可选元素指定默认值,则使用特定于类型的默认值:对于字符串,默认值为空字符串。对于字节,默认值为空字节字符串。对于布尔值,默认值为false。对于数字类型,默认值为零。对于枚举,默认值为枚举类型定义中列出的第一个值。这意味着在将值添加到枚举值列表的开头时必须格外小心。有关如何安全更改定义的指导,请参阅更新消息类型部分。

 

枚举

在定义消息类型时,您可能希望其字段之一仅具有一个预定义的值列表之一。例如,假设你想添加一个corpus字段每个SearchRequest,其中语料库可以UNIVERSALWEBIMAGESLOCALNEWSPRODUCTSVIDEO。您可以通过enum在消息定义中添加来非常简单地执行此操作-enum类型的字段只能使用一组指定的常量作为其值(如果尝试提供其他值,则解​​析器会将其视为未知值领域)。在以下示例中,我们添加了一个带有所有可能值的enum被叫项Corpus,以及一个type字段Corpus


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

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

enum EnumAllowingAlias {option allow_alias = true;UNKNOWN = 0;STARTED = 1;RUNNING = 1;
}
enum EnumNotAllowingAlias {UNKNOWN = 0;STARTED = 1;// RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}

枚举器常量必须在32位整数范围内。由于enum值在导线上使用varint编码,因此负值效率不高,因此不建议使用。您可以enum在消息定义内定义,如上面的示例所示,enum也可以在外部定义-这些s可以在.proto文件中的任何消息定义中重复使用。您还可以使用enum语法将一条消息中声明的类型用作另一条消息中字段的类型_MessageType_._EnumType_

当您在.proto使用的上运行协议缓冲区编译器时enum,生成的代码将具有对应enum于Java或C ++的代码,或者具有特定EnumDescriptor于Python的特殊类,用于在运行时生成的类中使用整数值创建一组符号常量。

**注意:**生成的代码可能会受到特定于语言的枚举数限制(一种语言的成千上万个)。请查看您计划使用的语言的限制。

有关如何enum在应用程序中使用message的更多信息,请参见针对所选语言生成的代码指南。

保留值

如果通过完全删除枚举条目或将其注释掉来更新枚举类型,则将来的用户在自己对类型进行更新时可以重用数值。如果他们以后加载相同版本的旧版本,可能会导致严重的问题.proto,包括数据损坏,隐私错误等。确保不会发生这种情况的一种方法是,将删除的条目的数字值(和/或名称,也可能导致JSON序列化的问题)指定为reserved。如果将来有任何用户尝试使用这些标识符,则协议缓冲区编译器会抱怨。您可以使用max关键字指定保留的数值范围达到最大可能值。


enum Foo {reserved 2, 15, 9 to 11, 40 to max;reserved "FOO", "BAR";
}

请注意,您不能在同reserved一条语句中混合使用字段名和数字值。

使用其他消息类型

您可以使用其他消息类型作为字段类型。例如,假设你想包括Result每个消息的SearchResponse消息-要做到这一点,你可以定义一个Result在同一个消息类型.proto,然后指定类型的字段ResultSearchResponse


message SearchResponse {repeated Result result = 1;
}message Result {required string url = 1;optional string title = 2;repeated string snippets = 3;
}

导入定义

在上面的示例中,Result消息类型与以下文件定义在同一文件中SearchResponse–如果要用作字段类型的消息类型已在另一个.proto文件中定义,该怎么办?

您可以.proto通过导入其他文件来使用它们的定义。要导入another.proto的定义,请在文件顶部添加一个import语句:

import "myproject/other_protos.proto";

默认情况下,您只能使用直接导入.proto文件中的定义。但是,有时您可能需要将.proto文件移动到新位置。.proto现在,您可以直接.proto在原位置放置一个虚拟文件,以使用该import public概念将所有导入转发到新位置,而不是直接移动文件并一次更改所有呼叫站点。import public任何导入包含该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

协议编译器使用-I/--proto_path标志在协议编译器命令行上指定的一组目录中搜索导入的文件。如果未给出标志,它将在调用编译器的目录中查找。通常,应将--proto_path标志设置为项目的根,并对所有导入使用完全限定的名称。

使用proto3消息类型

可以导入proto3消息类型并在proto2消息中使用它们,反之亦然。但是,不能在proto3语法中使用proto2枚举。

嵌套类型

您可以在其他消息类型中定义和使用消息类型,如以下示例所示–在此处,Result消息是在SearchResponse消息内部定义的:

message SearchResponse {message Result {required string url = 1;optional string title = 2;repeated string snippets = 3;}repeated Result result = 1;
}

如果要在其父消息类型之外重用此消息类型,则将其称为_Parent_._Type_

message SomeOtherMessage {optional SearchResponse.Result result = 1;
}

您可以根据需要深度嵌套消息:

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

团体

请注意,此功能已弃用,在创建新的消息类型时不应使用–而是使用嵌套的消息类型。

组是在信息定义中嵌套信息的另一种方法。例如,另一种指定SearchResponse包含多个的Result的方法如下:

message SearchResponse {repeated group Result = 1 {required string url = 2;optional string title = 3;repeated string snippets = 4;}
}

组将嵌套的消息类型和字段简单地组合为一个声明。在您的代码中,您可以像对待此消息一样将其视为具有一个Result类型字段result(将后者的名称转换为小写,以便与前者不冲突)一样对待。因此,此示例与SearchResponse上述示例完全等效,除了该消息具有不同的电汇格式。

更新消息类型

如果现有消息类型不再满足您的所有需求(例如,您希望消息格式具有一个额外的字段),但是您仍然希望使用以旧格式创建的代码,请不要担心!在不破坏任何现有代码的情况下更新消息类型非常简单。只要记住以下规则:

  • 不要更改任何现有字段的字段编号。
  • 您添加的任何新字段应为optionalrepeated。这意味着任何使用“旧”消息格式通过代码序列化的消息都可以被新生成的代码解析,因为它们不会丢失任何required元素。您应该为这些元素设置合理的默认值,以便新代码可以与旧代码生成的消息正确交互。同样,由新代码创建的消息可以由旧代码解析:旧的二进制文件在解析时只会忽略新字段。但是,未知字段不会被丢弃,并且如果消息在以后进行序列化,则未知字段也会与之一起进行序列化–因此,如果消息传递给新代码,则新字段仍然可用。
  • 只要在更新的消息类型中不再使用该字段号,就可以删除不需要的字段。您可能想要重命名该字段,或者添加前缀“ OBSOLETE_”,或者将字段编号保留为,以便将来的用户.proto不会意外地重用该编号。
  • 只要类型和数字保持不变,就可以将不需要的字段转换为扩展名,反之亦然。
  • int32uint32int64uint64,和bool都是兼容的-这意味着你可以在现场从这些类型到另一种改变不破坏forwards-或向后兼容。如果从对应的类型不适合的导线中解析出一个数字,则将获得与在C ++中将数字强制转换为该类型一样的效果(例如,如果将64位数字读取为int32,它将被截断为32位)。
  • sint32并且sint64彼此兼容,但与其他整数类型兼容。
  • string并且bytes只要字节是有效的UTF-8即可兼容。
  • bytes如果字节包含消息的编码版本,则嵌入式消息与之兼容。
  • fixed32与兼容sfixed32,并fixed64sfixed64
  • 对于stringbytes和消息字段,optional与兼容repeated。给定重复字段的序列化数据作为输入,如果期望此字段optional是原始类型字段,则期望该字段的客户端将采用最后一个输入值;如果是消息类型字段,则将合并所有输入元素。请注意,这对于数字类型(包括布尔值和枚举)通常并不安全。重复的数字类型字段可以以打包格式序列化,当需要一个optional字段时,将无法正确解析该格式。
  • 只要您记住从未通过网络发送默认值,通常就可以更改默认值。因此,如果程序收到未设置特定字段的消息,则该程序将看到该程序协议版本中定义的默认值。它不会看到在发送者的代码中定义的默认值。
  • enum与兼容int32uint32int64,和uint64电线格式条款(请注意,如果他们不适合的值将被截断),但是要知道,客户端代码可以区别对待反序列化的消息时。值得注意的是,enum当对消息进行反序列化时,无法识别的值将被丢弃,这会使字段的has..访问器返回false,并且其getter返回enum定义中列出的第一个值;如果指定了默认值,则返回默认值。对于重复的枚举字段,所有无法识别的值将从列表中删除。但是,整数字段将始终保留其值。因此,在将整数升级为a时,enum在接收在线上的枚举枚举值方面需要非常小心。
  • 在当前的Java和C ++实现中,当enum去除了无法识别的值时,它们会与其他未知字段一起存储。请注意,如果将此数据序列化然后由识别这些值的客户端重新解析,则可能导致奇怪的行为。对于可选字段,即使在反序列化原始消息后写入了新值,识别该值的客户端仍会读取旧值。在重复字段的情况下,旧值将出现在任何已识别的值和新添加的值之后,这意味着将不保留顺序。
  • 将单个optional值更改为 值的成员oneof是安全且二进制兼容的。如果您确定没有代码一次设置多个optional字段,那么将多个字段移动到新字段中oneof可能是安全的。将任何字段移到现有字段中oneof都是不安全的。
  • map<K, V>和对应的repeated消息字段之间更改字段是二进制兼容的(有关消息布局和其他限制,请参见下面的Maps)。但是,更改的安全性取决于应用程序:在反序列化和重新序列化消息时,使用repeated字段定义的客户端将产生语义上相同的结果;但是,使用map字段定义的客户端可以对条目进行重新排序,并删除具有重复键的条目。

扩展名

通过扩展,您可以声明消息中的字段号范围可用于第三方扩展。扩展名是其类型未由原始.proto文件定义的字段的占位符。这允许其他.proto文件通过使用这些字段编号定义某些或所有字段的类型来添加到您的消息定义中。让我们看一个例子:

message Foo {// ...extensions 100 to 199;
}

这表示字段编号[100,199] in的Foo范围保留用于扩展。现在,其他用户可以使用指定范围内的字段编号将新字段添加到Foo自己的.proto文件中,以导入您的.proto,例如:

extend Foo {optional int32 bar = 126;
}

这会将名称为bar126的字段添加到的原始定义Foo

对用户的Foo消息进行编码后,有线格式与用户在中定义新字段的方式完全相同Foo。但是,在应用程序代码中访问扩展字段的方式与访问常规字段略有不同–生成的数据访问代码具有用于处理扩展的特殊访问器。因此,例如,这是您bar在C ++中设置value的方法:

Foo foo;
foo.SetExtension(bar, 15);

类似地,Foo类定义模板访问器HasExtension()ClearExtension()GetExtension()MutableExtension(),和AddExtension()。所有的语义都与普通字段的相应生成的访问器匹配。有关使用扩展的更多信息,请参见针对所选语言生成的代码参考。

请注意,扩展名可以是任何字段类型,包括消息类型,但不能是oneofs或maps。

嵌套扩展

您可以在另一种类型的范围内声明扩展:

message Baz {extend Foo {optional int32 bar = 126;}...
}

在这种情况下,访问此扩展的C ++代码为:

foo.SetExtension(Baz :: bar,15);

换句话说,唯一的效果是bar在的范围内定义的Baz

这是造成混淆的常见原因:声明extend嵌套在消息类型内部的块并不意味着外部类型与扩展类型之间没有任何关系。特别地,上面的示例并不意味着Baz是的任何子类Foo。它的意思是符号bar在范围内声明Baz; 它只是一个静态成员。

一种常见的模式是在扩展名的字段类型范围内定义扩展名-例如,这Foo是typeBaz的扩展名,其中扩展名定义为的一部分Baz

message Baz {extend Foo {optional Baz foo_ext = 127;}...
}

但是,不需要在消息类型的内部定义具有消息类型的扩展名。您也可以这样做:

message Baz {...
}// This can even be in a different file.
extend Foo {optional Baz foo_baz_ext = 127;
}

实际上,为了避免混淆,可能首选此语法。如上所述,嵌套语法经常被不熟悉扩展的用户误认为是子类。

选择分机号码

确保两个用户不要使用相同的字段号将扩展名添加到同一消息类型是非常重要的–如果意外将扩展名解释为错误的类型,则可能导致数据损坏。您可能要考虑为项目定义扩展名编号约定,以防止发生这种情况。

如果您的编号约定可能涉及具有非常大的字段号的扩展名,则可以使用max关键字指定扩展范围达到最大可能的字段号:

message Foo {extensions 1000 to max;
}

max为2 29日- 1,或536870911。

与通常选择字段编号时一样,您的编号约定也需要避免使用字段编号19000到19999(FieldDescriptor::kFirstReservedNumberFieldDescriptor::kLastReservedNumber),因为它们是为协议缓冲区实现保留的。您可以定义一个包括该范围的扩展名范围,但是协议编译器不允许您使用这些数字定义实际的扩展名。

 

---more---

https://developers.google.cn/protocol-buffers/docs/proto?hl=zh-cn

编码方式

  • 一个简单的消息
  • 基础128种
  • 讯息结构
  • 更多值类型
  • 嵌入式消息
  • 可选和重复元素
  • 现场订单

本文档介绍了协议缓冲区消息的二进制线路格式。您无需了解这一点即可在应用程序中使用协议缓冲区,但是了解不同的协议缓冲区格式如何影响编码消息的大小可能非常有用。

一个简单的消息

假设您有以下非常简单的消息定义:

消息Test1 {可选int32 a = 1 ; }  

在应用程序中,创建一条Test1消息并将其设置a为150。然后将消息序列化为输出流。如果您能够检查编码后的消息,则会看到三个字节:

08 96 01  

到目前为止,如此小而数字-但是这意味着什么呢?继续阅读...

基础128种

要了解您的简单协议缓冲区编码,您首先需要了解varints。Varints是一种使用一个或多个字节序列化整数的方法。较小的数字占用较少的字节数。

除了最后一个字节外,varint中的每个字节都设置了最高有效位(msb)-这表明还有其他字节要来。每个字节的低7位用于以7位为一组存储数字的二进制补码表示,最低有效组在前

因此,例如,这里是数字1 –它是一个字节,因此未设置msb:

0000 0001 

 

讲解

本节中的每个教程都向您展示如何使用您喜欢的语言使用协议缓冲区来实现一个简单的应用程序,向您介绍该语言的协议缓冲区API,并向您展示创建和使用.proto文件的基础知识。还提供了每个应用程序的完整示例代码。

教程不假定您对协议缓冲区有任何了解,但假定您可以用所选语言编写代码,包括使用文件I / O,就很舒服。

  • C ++教程
  • C#教程
  • 飞镖教程
  • 入门教程
  • Java教程
  • Python教程

 

 

协议缓冲区基础:C ++

https://developers.google.cn/protocol-buffers/docs/cpptutorial?hl=zh-cn


本教程提供了使用协议缓冲区的基本C ++程序员介绍。通过创建一个简单的示例应用程序,它向您展示了如何

  • .proto文件中定义消息格式。
  • 使用协议缓冲区编译器。
  • 使用C ++协议缓冲区API写入和读取消息。

这不是在C ++中使用协议缓冲区的全面指南。有关更多详细的参考信息,请参阅《协议缓冲区语言指南》,《C ++ API参考》,《C ++生成的代码指南》和《编码参考》。

为什么要使用协议缓冲区?

我们将使用的示例是一个非常简单的“地址簿”应用程序,该应用程序可以在文件中读写人的联系方式。通讯录中的每个人都有一个姓名,一个ID,一个电子邮件地址和一个联系电话。

您如何像这样序列化和检索结构化数据?有几种方法可以解决此问题:

  • 原始内存中的数据结构可以以二进制形式发送/保存。随着时间的流逝,这是一种脆弱的方法,因为接收/读取代码必须使用完全相同的内存布局,字节序等进行编译。此外,由于文件会以原始格式存储数据,并且为该格式连接的软件副本也会被存储。散布,很难扩展格式。
  • 您可以发明一种将数据项编码为单个字符串的临时方法,例如将4个整数编码为“ 12:3:-23:67”。尽管确实需要编写一次性的编码和解析代码,但是这是一种简单且灵活的方法,而且解析带来的运行时成本很小。这对于编码非常简单的数据最有效。
  • 将数据序列化为XML。由于XML是人类(一种)可读的,并且存在用于多种语言的绑定库,因此这种方法可能非常有吸引力。如果要与其他应用程序/项目共享数据,这可能是一个不错的选择。但是,众所周知,XML占用大量空间,对它进行编码/解码会给应用程序带来巨大的性能损失。同样,导航XML DOM树比通常导航类中的简单字段要复杂得多。

协议缓冲区是灵活,高效,自动化的解决方案,可以准确地解决此问题。使用协议缓冲区,您可以编写.proto要存储的数据结构的描述。由此,协议缓冲区编译器创建了一个类,该类以有效的二进制格式实现协议缓冲区数据的自动编码和解析。生成的类为构成协议缓冲区的字段提供获取器和设置器,并以协议为单位来处理读写协议缓冲区的详细信息。重要的是,协议缓冲区格式支持随时间扩展格式的想法,以使代码仍可以读取以旧格式编码的数据。

在哪里可以找到示例代码

示例代码包含在源代码包中的“ examples”目录下。 在这里下载。

定义协议格式

要创建地址簿应用程序,您需要从.proto文件开始。.proto文件中的定义很简单:您为要序列化的每个数据结构添加一条消息,然后为消息中的每个字段指定名称和类型。这是.proto定义您的消息的文件addressbook.proto

syntax = "proto2";package tutorial;message Person {required string name = 1;required int32 id = 2;optional string email = 3;enum PhoneType {MOBILE = 0;HOME = 1;WORK = 2;}message PhoneNumber {required string number = 1;optional PhoneType type = 2 [default = HOME];}repeated PhoneNumber phones = 4;
}message AddressBook {repeated Person people = 1;
}

如您所见,语法类似于C ++或Java。让我们浏览文件的每个部分,看看它的作用。

.proto文件以程序包声明开头,这有助于防止不同项目之间的命名冲突。在C ++中,您生成的类将放置在与程序包名称匹配的名称空间中。

接下来,您将拥有消息定义。消息只是包含一组类型字段的汇总。许多标准的简单数据类型都可以作为字段类型,包括boolint32floatdouble,和string。您还可以通过使用其他消息类型作为字段类型来为消息添加进一步的结构-在上面的示例中,Person消息包含PhoneNumber消息,而AddressBook消息包含Person消息。您甚至可以定义嵌套在其他消息内的消息类型-如您所见,该PhoneNumber类型在内部定义Personenum如果您希望某个字段具有一个预定义的值列表之一,也可以定义类型-在这里您要指定电话号码可以是MOBILEHOMEWORK

每个元素上的“ = 1”,“ = 2”标记标识该字段在二进制编码中使用的唯一“标记”。标签编号1至15与较高的编号相比,编码所需的字节减少了一个字节,因此,为了进行优化,您可以决定将这些标签用于常用或重复的元素,而将标签16和更高的标签用于较少使用的可选元素。重复字段中的每个元素都需要重新编码标签号,因此重复字段是此优化的最佳候选者。

每个字段都必须使用以下修饰符之一进行注释:

  • required:必须提供该字段的值,否则该消息将被视为“未初始化”。如果libprotobuf在调试模式下编译,则序列化未初始化的消息将导致断言失败。在优化的版本中,将跳过检查,并且无论如何将写入消息。但是,解析未初始化的消息将始终失败(通过false从parse方法返回)。除此之外,必填字段的行为与可选字段完全相同。
  • optional:可能会或可能不会设置该字段。如果未设置可选字段值,则使用默认值。对于简单类型,您可以指定自己的默认值,就像type在示例中为电话号码所做的那样。否则,将使用系统默认值:数字类型为零,字符串为空字符串,布尔值为false。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,没有设置任何字段。调用访问器以获取未显式设置的可选(或必填)字段的值始终会返回该字段的默认值。
  • repeated:该字段可以重复任意次(包括零次)。重复值的顺序将保留在协议缓冲区中。将重复字段视为动态大小的数组。

永远是必需的 您将字段标记为时应格外小心required。如果您希望停止写入或发送必填字段,则将该字段更改为可选字段会很麻烦–老读者会认为没有该字段的邮件是不完整的,可能会无意中拒绝或丢弃它们。您应该考虑为缓冲区编写特定于应用程序的自定义验证例程。Google的一些工程师得出的结论是,使用required弊大于利。他们更喜欢只使用optionalrepeated。但是,这种观点并不普遍。

.proto可以在“协议缓冲区语言指南”中找到有关编写文件的完整指南,包括所有可能的字段类型。但是,不要去寻找类似于类继承的工具–协议缓冲区不能做到这一点。

 

 

Protocol Buffer使用简介

https://www.jianshu.com/p/b1f18240f0c7


1.概览

1.1 什么是protocol buffer

protocol buffer是google的一个开源项目,它是用于结构化数据串行化的灵活、高效、自动的方法,例如XML,不过它比xml更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。

2.使用

2.1定义一个消息类型

message SearchRequest 
{required string query = 1;optional int32 page_number = 2;// Which page number do we want?optional int32 result_per_page = 3;// Number of results to return per page.
}

该消息定义了三个字段,两个int32类型和一个string类型的字段,每个字段由字段限制,字段类型,字段名和Tag四部分组成.对于C++,每一个.proto文件经过编译之后都会对应的生成一个.h和一个.cc文件.

字段限制

字段限制共有3类:
required:必须赋值的字段
optional:可有可无的字段
repeated:可重复字段(变长字段),类似于数组
由于一些历史原因,repeated字段并没有想象中那么高效,新版本中允许使用特殊的选项来获得更高效的编码:

repeated int32 samples = 4 [packed=true];

Tags

消息中的每一个字段都有一个独一无二的数值类型的Tag.1到15使用一个字节编码,16到2047使用2个字节编码,所以应该将Tags 1到15留给频繁使用的字段.
可以指定的最小的Tag为1, 最大为2^{29}-1或536,870,911.但是不能使用19000到19999之间的值,这些值是预留给protocol buffer的.

注释

使用C/C++的//语法来添加字段注释.

2.2 值类型

proto的值类型与具体语言中值类型的对应关系.

2.3 可选字段与缺省值

在消息解析时,如果发现消息中没有包含可选字段,此时会将消息解析对象中相对应的字段设置为默认值,可以通过下面的语法为optional字段设置默认值:

optional int32 result_per_page = 3 [default = 10];

如果没有指定默认值,则会使用系统默认值,对于string默认值为空字符串,对于bool默认值为false,对于数值类型默认值为0,对于enum默认值为定义中的第一个元素.

2.4 枚举

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

由于枚举值采用varint编码,所以为了提高效率,不建议枚举值取负数.这些枚举值可以在其他消息定义中重复使用.

2.5 使用其他消息类型

可以使用一个消息的定义作为另一个消息的字段类型.

message Result 
{required string url = 1;optional string title = 2;repeated string snippets = 3;
}message SearchResponse 
{repeated Result result = 1;
}

可以使用import语法来包含另外一个.proto文件.

import "myproject/other_protos.proto";

2.6 嵌套类型

在protocol中可以定义如下的嵌套类型

message SearchResponse 
{message Result {required string url = 1;optional string title = 2;repeated string snippets = 3;}repeated Result result = 1;
}

如果在另外一个消息中需要使用Result定义,则可以通过Parent.Type来使用.

message SomeOtherMessage 
{optional SearchResponse.Result result = 1;
}

protocol支持更深层次的嵌套和分组嵌套,但是为了结构清晰起见,不建议使用过深层次的嵌套,建议通过 2.5 小节提到的方法来实现.

2.7 更新一个数据类型

在更新一个数据类型时更多的是需要考虑与旧版本的兼容性问题:

  1. 不要改变任何已存在字段的Tag值,如果改变Tag值可能会导致数值类型不匹配,具体原因参加protocol编码
  2. 建议使用optionalrepeated字段限制,尽可能的减少required的使用.
  3. 不需要的字段可以删除,删除字段的Tag不应该在新的消息定义中使用.
  4. 不需要的字段可以转换为扩展,反之亦然只要类型和数值依然保留
  5. int32, uint32, int64, uint64, 和bool是相互兼容的,这意味着可以将其中一种类型任意改编为另外一种类型而不会产生任何问题
  6. sint32sint64是相互兼容的
  7. stringbytes是相互兼容的
  8. fixed32 兼容 sfixed32, fixed64 兼容 sfixed64.
  9. optional 兼容repeated

2.8 扩展

extend特性来让你声明一些Tags值来供第三方扩展使用.

message Foo 
{// ...extensions 100 to 199;
}

假如你在你的proto文件中定义了上述消息,之后别人在他的.proto文件中import你的.proto文件,就可以使用你指定的Tag范围的值.

extend Foo 
{optional int32 bar = 126;
}

在访问extend中定义的字段和,使用的接口和一般定义的有点不一样,例如set方法:

    Foo foo;foo.SetExtension(bar, 15);

类似的有HasExtension(), ClearExtension(), GetExtension(), MutableExtension(), and AddExtension()等接口.

2.9 选项

  • optimize_for (file option): 可以设置的值有SPEED, CODE_SIZE, 或 LITE_RUNTIME. 不同的选项会以下述方式影响C++, Java代码的生成.T
    • SPEED (default): protocol buffer编译器将会生成序列化,语法分析和其他高效操作消息类型的方式.这也是最高的优化选项.确定是生成的代码比较大.
    • CODE_SIZE: protocol buffer编译器将会生成最小的类,确定是比SPEED运行要慢
    • LITE_RUNTIME: protocol buffer编译器将会生成只依赖"lite" runtime library (libprotobuf-lite instead of libprotobuf)的类. lite运行时库比整个库更小但是删除了例如descriptors 和 reflection等特性. 这个选项通常用于手机平台的优化.
option optimize_for = CODE_SIZE;

3.常用API介绍

对于如下消息定义:

// test.proto
message PBStudent 
{    optional uint32 StudentID   = 1;optional string Name        = 2;optional uint32 Score       = 3;
}    message PBMathScore
{    optional uint32 ClassID     = 1;  repeated PBStudent ScoreInf   = 2;
}

protocol buffer编译器会为每个消息生成一个类,每个类包含基本函数,消息实现,嵌套类型,访问器等部分.

3.1 基本函数

public:PBStudent();virtual ~PBStudent();PBStudent(const PBStudent& from);inline PBStudent& operator=(const PBStudent& from) {CopyFrom(from);return *this;}inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {return _unknown_fields_;}inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {return &_unknown_fields_;}static const ::google::protobuf::Descriptor* descriptor();static const PBStudent& default_instance();void Swap(PBStudent* other);

3.2 消息实现

PBStudent* New() const;
void CopyFrom(const ::google::protobuf::Message& from);
void MergeFrom(const ::google::protobuf::Message& from);
void CopyFrom(const PBStudent& from);
void MergeFrom(const PBStudent& from);
void Clear();
bool IsInitialized() const;                                                                          int ByteSize() const;
bool MergePartialFromCodedStream(::google::protobuf::io::CodedInputStream* input);
void SerializeWithCachedSizes(::google::protobuf::io::CodedOutputStream* output) const;
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
int GetCachedSize() const { return _cached_size_; }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const;

3.3 嵌套类型

3.4 访问器

// optional uint32 StudentID = 1;
inline bool has_studentid() const;
inline void clear_studentid();
static const int kStudentIDFieldNumber = 1;
inline ::google::protobuf::uint32 studentid() const;
inline void set_studentid(::google::protobuf::uint32 value);// optional string Name = 2;
inline bool has_name() const;                                
inline void clear_name();
static const int kNameFieldNumber = 2;
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline void set_name(const char* value, size_t size);
inline ::std::string* mutable_name();
inline ::std::string* release_name();
inline void set_allocated_name(::std::string* name);// optional uint32 Score = 3;                            
inline bool has_score() const;
inline void clear_score();
static const int kScoreFieldNumber = 3;
inline ::google::protobuf::uint32 score() const;
inline void set_score(::google::protobuf::uint32 value);

protocol buffer编译器会对每一个字段生成一些getset方法,这些方法的名称采用标识符所有小写加上相应的前缀或后缀组成.生成一个值为Tags的k标识符FieldNum常量,

3.5 其他函数

除了生成上述类型的方法外, 编译器还会生成一些用于消息类型处理的私有方法. 每一个.proto文件在编译的时候都会自动包含message.h文件,这个文件声明了很多序列化和反序列化,调试, 复制合并等相关的方法.

3.6 使用例子

在我们平时的使用中,通常一个message对应一个类,在对应的类中定义一个set和create方法来生成和解析PB信息.针对上述消息定义如下类:

// test.h
class CStudent
{
public:unsigned    mStudentID;unsigned    mScore;string      mName;CStudent(){Init();}inline void Init(){mStudentID = 0;mScore = 0;mName = "";}
}class CMathScore
{
private:unsigned    mClassID;CStudent    mScoreInf[100];
public:CMathSCore(){Init();}~CMathScore() {};void Init();void SetFromPB(const PBMathScore* pPB);void CreatePB(PBMathScore* pPB);// Get & Set mClassID...// Get & set mScoreInf...// some other function...
}

对应的cpp文件中实现对PB的操作

// test.cpp
void CMathScore::Init()
{mClassID = 0;memset(mScoreInf, 0, sizeof(mScoreInf));
}void CMathScore::SetFromPB(const PBMathScore* pPB)
{if ( NULL == pPB ) return;mClassID = pPB->classid();for(unsigned i = 0; i < (unsigned)pPB->scoreinf_size() && i < 100; ++i){PBStudent* pStu = pPB->mutable_scoreinf(i);mScoreInf[i].mStudentID = pStu->studentid();mScoreInf[i].mScore     = pStu->score();mScoreInf[i].mName      = pStu->name();}
}void CMathScore::CreatePB(PBMathScore* pPB)
{if ( NULL == pPB ) return;pPB->set_classid(mClassID);for(unsigned i = 0; i < 100; ++i){PBStudent* pStu = pPB->add_scoreinf();pStu->set_studentid(mScoreInf[i].mStudentID)pStu->set_score(mScoreInf[i].mScore);pStu->set_name(mScoreInf[i].mName);     }
}

PB文件的读写

// use.cpp
#include<test.h>#defind     MAX_BUFFER      1024 * 1024
int write()
{CMathScore  mMath;PBMathScore mPBMath;// use set functions to init member variablefstream fstm("./math.dat", ios::out | ios::binary);if ( fstm.is_open() == false ){return -1;}   char* tpBuffer = (char*)malloc(MAX_BUFFER);if ( NULL == tpBuffer ){return -2;}mMath.CreatePB(&mPBMath);if ( mPBMath.SerializeToArray(tpBuffer, mPBMath.ByteSize()) == false ){return -3;}fstm.write(tpBuffer, mPBMath.ByteSize());free(tpBuffer);fstm.close();return 0;
}int read()
{CMathScore  mMath;PBMathScore mPBMath;fstream fstm.open("./math.dat", ios::out | ios::binary);if ( fstm.is_open() == false ){return -1;}   char* tpBuffer = (char*)malloc(MAX_BUFFER);if ( NULL == tpBuffer ){return -2;}char*   tpIdx = tpBuffer;int     tLen;while ( !fstm.eof() && tLen < MAX_BUFFER ){fstm.read(tpIdx, 1);tpIdx += 1;tLen++;}if ( mPBMath.ParseFromArray(tpBuffer, tLen - 1) == false ){return -3;}fstm.close();free(tpBuffer);tpIdx = NULL;mMath.SetFromPB(&mPBMath);// do some thingreturn 0;
}

作者:倚楼
链接:https://www.jianshu.com/p/b1f18240f0c7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Vue使用NProgress

    NProgress是页面跳转是出现在浏览器顶部的进度条 官网&#xff1a;http://ricostacruz.com/nprogress/ github&#xff1a;https://github.com/rstacruz/nprogress 如下图所示&#xff0c;绿色的进度条就是NProgress实现的效果 1、安装 $ npm install --save nprogress 或者 …...

    2024/4/2 22:20:14
  2. #Schwinn记账本#Javabean实体类

    java bean实体类用于映射数据库表中之每行数据&#xff1b;诸如分类表Category&#xff0c;有很多条分类记录&#xff0c;于是就生发一实体类Category&#xff0c;这个实体类的实例就对应了表Category之记录&#xff1b;...

    2024/4/21 14:39:45
  3. Typora添加主题

    Typora的自带主题有5个&#xff0c;如果自己想要添加一些主题&#xff0c;方法如下&#xff1a; 打开Typora&#xff0c;点击左上角的【文件】&#xff0c;弹出菜单点击下方的【偏好设置】 进入页面&#xff0c;左侧选择【外观】&#xff0c;点击右下角【获取主题】&#xff0…...

    2024/4/13 19:56:19
  4. [BJDCTF2020]The mystery of ip(xff,smarty模板注入)

    首先服务器是如何知道我的ip的呢,猜想可能是XFF或Client-IP这两个header. 抓个包 发现是通过XFF smarty模板注入 参考了一下这位师傅的博客. PHP的模板注入&#xff08;Smarty模板&#xff09; 看看flag.php源代码是啥. <?phprequire_once(header.php);require_once(…...

    2024/4/3 4:07:22
  5. 移动端测试—简单了解

    移动端测试—简单了解Node安装配置Appium环境搭建使用AppiumNode安装配置 具体的安装不用介绍了&#xff0c;傻瓜式安装&#xff0c;只需要改一下安装路径就可以 环境配置 将你node的安装路径配置到path路径下 在命令行中查看版本&#xff0c;表示配置成功 Appium环境搭建 …...

    2024/4/3 4:26:10
  6. 【leetcode】169. 多数元素(majority-element)(模拟)[简单]

    链接 https://leetcode-cn.com/problems/majority-element/ 耗时 解题&#xff1a;3 min 题解&#xff1a;4 min 题意 给定一个大小为 n 的数组&#xff0c;找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c…...

    2024/4/2 23:24:22
  7. Java 内部类和异常类

    内部类 定义&#xff1a; Java支持在一个类中定义另一个类&#xff0c;这样的类称为内部类&#xff0c;而包含内部类的类称为内部类的外嵌类。 内部类和外嵌类的关系&#xff1a; 内部类的外嵌类的成员变量在内部类中仍然有效 内部类中的方法也可以调用外嵌类中的方法 内部…...

    2024/4/16 23:51:06
  8. 内敛函数

    内联函数&#xff1a;在调用函数时相当于直接复制粘贴函数的语句 例&#xff1a; inline int add(int a,int b) { return ab; } int main() { cout<<add(3,5)<<endl; //相当于cout<<35<<endl; }优点&#xff1a;降低调用函数的时间成本。 缺点&#…...

    2024/4/3 6:15:16
  9. Opencv学习笔记 常用函数、基础知识二

    Gamma校正 在电视和图形监视器中&#xff0c;显像管发生的电子束及其生成的图像亮度并不是随显像管的输入电压线性变化&#xff0c;电子流与输入电压相比是按照指数曲线变化的&#xff0c;输入电压的指数要大于电子束的指数。这说明暗区的信号要比实际情况更暗&#xff0c;而亮…...

    2024/4/19 6:10:39
  10. 2020-10-04总结

    2020-10-04总结 这周开始认真刷题做比赛了。每次碰上不是很难但是又不会的题总想穿越回两年前给自己一巴掌。 这周打了一场CF&#xff0c;过了4道题。可惜只是个div3。不过做D题的思路对我启发还蛮大的。对于各种容器的一些应用又回顾了一遍。最后E题是一道DP的题。DP最近在我…...

    2024/4/4 22:11:29
  11. 5、员工管理系统

    文章目录1、首页实现2、国际化1、在resource下新建一个i18n目录&#xff08;表示国际化&#xff09;2、将i18n配置到application.properties中3、编写国际化解析器4、 将国际化解析器配置到Bean容器中3、实现登录功能1、修改表单的active为controller里边的路径&#xff08;Req…...

    2024/4/9 12:45:06
  12. 10、基于深度卷积神经网络的农作物病害识别

    基于深度卷积神经网络的农作物病害识别 1、研究思路 运用深度学习的方法,基 于ResNet-50以及 InceptionV3、MobileNet等神经网络,并在这些基础结构上改进部分模型。该研 究主要从两个方面对卷积神经网络进行优化,一个是模型结构,另一个是训练标签优化。模型训练基于迁移…...

    2024/4/12 2:47:36
  13. 【AGC005D】~K Perm Counting(容斥,二分图,计数dp)

    首先正面做不太好做&#xff0c;考虑容斥。 设 f(m)f(m)f(m) 表示排列中至少有 mmm 处 ∣Pi−i∣k|P_i-i|k∣Pi​−i∣k 的方案数。 那么答案就是 ∑i0n(−1)if(i)\sum\limits_{i0}^n(-1)^if(i)i0∑n​(−1)if(i)。 原题可以看成一个二分图的形式&#xff1a;&#xff08;n5…...

    2024/4/21 17:08:17
  14. Opencv学习笔记 简单铅笔效果/卡通效果

    铅笔画滤镜 铅笔画滤镜主要是提取出图片中梯度比较高的部分&#xff0c;也就是边缘检测 卡通画特效 卡通画特效主要由三部分组成 1、提取图像中轮廓 > edges 2、使用bilateral 滤波在保留图像的边缘特征的前提下对图像进行模糊操作 > color 3、使用轮廓的图像作为mask对模…...

    2024/4/22 18:20:21
  15. 242.有效的字母异位词

    给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true示例 2: 输入: s "rat", t "car" 输出: false 说明: 你可以假设字符串只包含小写字母。…...

    2024/4/23 0:04:33
  16. Java 对象的内存模型

    对象的内存模型 声明对象时的内存模型&#xff1a;我们在声明对象变量后&#xff0c;对象的内存还没有任何数据&#xff0c;此时我们称这个对象是一个空对象。 空对象不能使用&#xff0c;因为它还没有得到任何“实体”&#xff0c;必须再进行为对象分配变量的操作&#xff0c;…...

    2024/4/21 10:37:13
  17. 百胜帝宝官网

    百胜帝宝官网【扣】【130一7710一405】信誉平臺&#xff01;很在字节跳动的实时计算场景中&#xff0c;我们有很多任务&#xff08;数量 2k&#xff09;会直接服务于线上&#xff0c;其输出时延和稳定性会直接影响线上产品的用户体验&#xff0c;这类任务通常具有如下特点&…...

    2024/4/21 9:47:20
  18. 3.1.4 内存的连续分配管理方式

    目录思维导图单一连续分配固定分区分配动态分区分配思维导图 单一连续分配 固定分区分配 动态分区分配...

    2024/4/16 13:35:00
  19. 【rosbridge】ROS与Android通信 (加更续集)

    摘要 在安卓端开发一个app&#xff0c;使用rosbridge&#xff0c;能够与ROS端进行交互。此前博客&#xff0c;已经基本完成rosbridge的测试&#xff0c;但是功能还不够完善。这篇博客主要介绍一个功能相对完善的开源安卓项目&#xff0c;并进行调试编译&#xff0c;构建apk文件…...

    2024/4/10 23:46:13
  20. 【LeetCode刷题(中等程度)】64. 最小路径和

    给定一个包含非负整数的 m x n 网格&#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例: 输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 7 解释: 因为路径 1→3→1→1→1 的总和最小…...

    2024/4/24 18:01:05

最新文章

  1. Ubentu18.0+ORBSLAM2

    Ubentu18.0ORB-SLAM2摄像头 引言&#xff1a; ​ 在视觉同步定位与地图构建&#xff08;Simultaneous Localization and Mapping, SLAM&#xff09;领域&#xff0c;ORB-SLAM2系统的出现标志着重要的技术进步。这个著名的SLAM系统由Juan D. Tards、Ral Mur-Artal等人开发&#…...

    2024/4/27 7:12:03
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. dp小兰走迷宫

    昨天学习了bfs的基本概念&#xff0c;今天来做一道经典习题练练手吧&#xff01; bfs常用的两类题型 1.从A出发是否存在到达B的路径(dfs也可) 2.从A出发到B的最短路径&#xff08;数小:<20才能用dfs&#xff09; 遗留的那个问题的答案- 题目&#xff1a;走迷宫 #incl…...

    2024/4/26 22:19:58
  4. 【JavaScript】如何在npm中切换源以及使用指定源安装依赖

    忘不掉的是什么我也不知道 想不起当年模样 看也看不到 去也去不了的地方 也许那老街的腔调是属于我的忧伤 嘴角那点微笑越来越勉强 忘不掉的是什么我也不知道 放不下熟悉片段 回头望一眼 已经很多年的时间 透过手指间看着天 我又回到那老街 靠在你们身边渐行渐远 …...

    2024/4/23 6:22:01
  5. 416. 分割等和子集问题(动态规划)

    题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义&#xff1a;dp[i][j]表示当背包容量为j&#xff0c;用前i个物品是否正好可以将背包填满&#xff…...

    2024/4/27 1:53:53
  6. 【Java】ExcelWriter自适应宽度工具类(支持中文)

    工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...

    2024/4/27 3:39:11
  7. Spring cloud负载均衡@LoadBalanced LoadBalancerClient

    LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon&#xff0c;直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件&#xff0c;我们讨论Spring负载均衡以Spring Cloud2020之后版本为主&#xff0c;学习Spring Cloud LoadBalance&#xff0c;暂不讨论Ribbon…...

    2024/4/26 8:22:40
  8. TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案

    一、背景需求分析 在工业产业园、化工园或生产制造园区中&#xff0c;周界防范意义重大&#xff0c;对园区的安全起到重要的作用。常规的安防方式是采用人员巡查&#xff0c;人力投入成本大而且效率低。周界一旦被破坏或入侵&#xff0c;会影响园区人员和资产安全&#xff0c;…...

    2024/4/26 11:10:01
  9. VB.net WebBrowser网页元素抓取分析方法

    在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

    2024/4/27 3:39:08
  10. 【Objective-C】Objective-C汇总

    方法定义 参考&#xff1a;https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...

    2024/4/27 3:39:07
  11. 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】

    &#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格…...

    2024/4/27 3:39:07
  12. 【ES6.0】- 扩展运算符(...)

    【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数&#xff0…...

    2024/4/26 6:06:14
  13. 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?

    文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕&#xff0c;各大品牌纷纷晒出优异的成绩单&#xff0c;摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称&#xff0c;在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁&#xff0c;多个平台数据都表现出极度异常…...

    2024/4/26 17:59:13
  14. Go语言常用命令详解(二)

    文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令&#xff0c;这些命令可以帮助您在Go开发中进行编译、测试、运行和…...

    2024/4/26 22:35:59
  15. 用欧拉路径判断图同构推出reverse合法性:1116T4

    http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b&#xff0c;我们在 a i a_i ai​ 和 a i 1 a_{i1} ai1​ 之间连边&#xff0c; b b b 同理&#xff0c;则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然&#xff0…...

    2024/4/26 17:00:23
  16. 【NGINX--1】基础知识

    1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息&#xff0c;并安装一些有助于配置官方 NGINX 软件包仓库的软件包&#xff1a; apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...

    2024/4/27 3:39:03
  17. Hive默认分割符、存储格式与数据压缩

    目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限&#xff08;ROW FORMAT&#xff09;配置标准HQL为&#xff1a; ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...

    2024/4/26 9:43:47
  18. 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

    文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…...

    2024/4/26 9:43:47
  19. --max-old-space-size=8192报错

    vue项目运行时&#xff0c;如果经常运行慢&#xff0c;崩溃停止服务&#xff0c;报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中&#xff0c;通过JavaScript使用内存时只能使用部分内存&#xff08;64位系统&…...

    2024/4/27 1:03:20
  20. 基于深度学习的恶意软件检测

    恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…...

    2024/4/27 3:22:12
  21. JS原型对象prototype

    让我简单的为大家介绍一下原型对象prototype吧&#xff01; 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定&#xff0c;每一个构造函数都有一个 prototype 属性&#xff0c;指向另一个对象&#xff0c;所以我们也称为原型对象…...

    2024/4/26 21:29:56
  22. C++中只能有一个实例的单例类

    C中只能有一个实例的单例类 前面讨论的 President 类很不错&#xff0c;但存在一个缺陷&#xff1a;无法禁止通过实例化多个对象来创建多名总统&#xff1a; President One, Two, Three; 由于复制构造函数是私有的&#xff0c;其中每个对象都是不可复制的&#xff0c;但您的目…...

    2024/4/27 3:39:00
  23. python django 小程序图书借阅源码

    开发工具&#xff1a; PyCharm&#xff0c;mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 小程序 功能介绍&#xff1a; 用户端&#xff1a; 登录注册&#xff08;含授权登录&#xff09; 首页显示搜索图书&#xff0c;轮播图&#xff0…...

    2024/4/26 23:53:24
  24. 电子学会C/C++编程等级考试2022年03月(一级)真题解析

    C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...

    2024/4/26 9:43:45
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57