Protocol Buffers语言特性 (proto 3)

news2024/10/6 18:30:47

定义消息类型

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

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 results_per_page = 3;
}

    文件的第一行指定您正在使用proto3语法,否则协议缓冲区编译器将假定您正在使用proto2。这必须是文件的第一个非空、非注释行。
    SearchRequest消息定义指定了三个字段(名称/值对),每个字段对应您希望包含在此类型消息中的数据块。每个字段都有一个名称和类型。

指定字段类型

    在前面的示例中,所有字段都是标量类型:两个整数(page_number和results_per_page)和一个字符串(query),可以像为字段指定其他消息类型(枚举和复合类型)。

分配字段编号

    必须为消息定义中的每个字段提供1到536,870,911之间的数字,并具有以下限制:

   1. 给定的数字在该消息的所有字段中必须是唯一的。
    2.字段号19,000到19999是为协议缓冲区实现保留的。如果您在消息中使用这些保留字段号之一,协议缓冲区编译器将报错。
    3.不能使用任何先前保留的字段号或任何已分配给扩展的字段号。

    一旦使用了消息类型,就不能更改此号码,因为它标识了消息连接格式中的字段。“更改”字段编号相当于删除该字段并创建具有相同类型但具有新编号的新字段。

    字段号永远不应该被重用。永远不要从保留列表中取出字段号以便与新字段定义一起重用。

    对于最常设置的字段,应该使用字段号1到15。较低的字段数值在连线格式中占用的空间较少。例如,1到15范围内的字段号需要一个字节来编码。16到2047范围内的字段号占用两个字节。

指定字段标签

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

    optional:可选字段有两种可能的状态:

    该字段已设置,并包含从连接显式设置或解析的值。它将被序列化到线路上。
    该字段未设置,将返回默认值。它不会被序列化到线路上。

    repeated:此字段类型可以在格式良好的消息中重复0次或多次。重复值的顺序将被保留。

    Map:这是一个配对的键/值字段类型。有关此字段类型的更多信息,

添加更多消息类型

    可以在单个.proto文件中定义多个消息类型。如果你要定义多个相关的消息,这是很有用的——例如,如果你想定义与你的SearchResponse消息类型对应的回复消息格式,你可以将它添加到相同的.proto:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 results_per_page = 3;
}

message SearchResponse {
 ...
}

    虽然可以在单个.proto文件中定义多个消息类型(例如message、enum和service),但是当在单个文件中定义具有不同依赖关系的大量消息时,也会导致依赖关系膨胀。建议每个.proto文件包含尽可能少的消息类型。

添加注释

    要在.proto文件中添加注释,请使用C/ c++风格的//和/*…* /语法。

/* SearchRequest represents a search query, with pagination options to
 * indicate which results to include in the response. */

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

删除字段

    如果操作不当,删除字段可能会导致严重的问题。

    当您不再需要字段,并且所有引用都已从客户端代码中删除时,您可以从消息中删除字段定义。但是,您必须保留已删除的字段号。如果您不保留字段编号,开发人员可能会在将来重用该编号。

    您还应该保留字段名,以允许消息的JSON和TextFormat编码继续解析。

保留字段号

    如果您通过完全删除一个字段或将其注释掉来更新消息类型,那么将来的开发人员可以在对该类型进行更新时重用该字段号。这可能导致严重的问题,如重用字段号的后果中所述。为了确保不会发生这种情况,请将已删除的字段号添加到保留列表中。

    如果将来的开发人员试图使用这些保留的字段号,协议编译器将生成错误消息。

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

    保留字段的取值范围是包含的(9 ~ 11与9、10、11相同)。

保留字段名称

    以后重用旧的字段名通常是安全的,除非使用序列化字段名的TextProto或JSON编码。为避免此风险,可将已删除的字段名添加到保留列表中。

    保留名称仅影响协议编译器行为,而不影响运行时行为,但有一个例外:TextProto实现可能在解析时使用保留名称丢弃未知字段(不会像其他未知字段一样引发错误)(今天只有c++和Go实现这样做)。运行时JSON解析不受保留名称的影响。

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

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

在.proto上运行 protocol buffer编译器时,编译器会用您所选择的语言生成代码,这些代码将用于处理文件中描述的消息类型,包括获取和设置字段值、将消息序列化到输出流以及从输入流解析消息。

    对于c++,编译器从每个.proto生成.h和.cc文件,并为文件中描述的每种消息类型提供一个类。

标量值类型

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

默认值

    在解析消息时,如果编码的消息不包含特定的隐式呈现元素,则访问已解析对象中的相应字段将返回该字段的默认值。这些默认值是特定于类型的:

    对于字符串,默认值是空字符串。
    对于字节,默认值为空字节。
   对于bool,默认值为false。
   对于数字类型,默认值为零。
   对于枚举,默认值是第一个定义的枚举值,该值必须为0。
   对于消息字段,没有设置该字段。它的确切值与语言有关。

    请注意,对于标量消息字段,一旦解析了消息,就无法判断字段是显式设置为默认值(例如布尔值是否设置为false)还是根本没有设置:在定义消息类型时应该记住这一点。例如,如果你不希望某些行为在默认情况下也发生,不要使用布尔值在设置为false时打开某些行为。还要注意,如果将标量消息字段设置为其默认值,则该值将不会在网络上序列化。如果浮点数或双精度值被设置为+0,它将不被序列化,但-0被认为是不同的,将被序列化。

枚举

    在定义消息类型时,您可能希望其中一个字段只具有预定义值列表中的一个。例如,假设您想为每个SearchRequest添加一个语料库字段,其中语料库可以是UNIVERSAL、WEB、IMAGES、LOCAL、NEWS、PRODUCTS或VIDEO。只需在消息定义中添加一个枚举,并为每个可能的值添加一个常量,就可以非常简单地做到这一点。

    在下面的示例中,我们添加了一个名为Corpus的枚举,其中包含所有可能的值,以及一个类型为Corpus的字段:

enum Corpus {
  CORPUS_UNSPECIFIED = 0;
  CORPUS_UNIVERSAL = 1;
  CORPUS_WEB = 2;
  CORPUS_IMAGES = 3;
  CORPUS_LOCAL = 4;
  CORPUS_NEWS = 5;
  CORPUS_PRODUCTS = 6;
  CORPUS_VIDEO = 7;
}

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 results_per_page = 3;
  Corpus corpus = 4;
}

    如上所示,Corpus枚举的第一个常量映射为0:每个枚举定义必须包含一个映射为0的常量作为其第一个元素。这是因为:

    必须有一个零值,以便我们可以使用0作为数字默认值。
    为了与proto2语义兼容,零值需要是第一个元素,在proto2语义中,除非显式指定不同的值,否则第一个枚举值是默认值。
    可以通过将相同的值赋给不同的枚举常量来定义别名。为此,您需要将allow_alias选项设置为true。否则,当找到别名时, protocol buffer编译器将生成一条警告消息。虽然所有别名值在反序列化期间都是有效的,但序列化时总是使用第一个值。

enum EnumAllowingAlias {
  option allow_alias = true;
  EAA_UNSPECIFIED = 0;
  EAA_STARTED = 1;
  EAA_RUNNING = 1;
  EAA_FINISHED = 2;
}

enum EnumNotAllowingAlias {
  ENAA_UNSPECIFIED = 0;
  ENAA_STARTED = 1;
  // ENAA_RUNNING = 1;  // Uncommenting this line will cause a warning message.
  ENAA_FINISHED = 2;
}

    枚举数常量必须在32位整数的范围内。由于枚举值在网络上使用可变编码,因此负值是低效的,因此不建议使用。可以在消息定义内定义枚举,如前面的示例所示,也可以在消息定义外定义枚举——这些枚举可以在.proto文件中的任何消息定义中重用。您还可以使用在一个消息中声明的枚举类型作为另一个消息中字段的类型,使用语法_MessageType_._EnumType_。

    当您在使用枚举的.proto上运行protocol buffer编译器时,生成的代码将具有对应的用于Java、Kotlin或c++的枚举,或者用于Python的特殊EnumDescriptor类,该类用于在运行时生成的类中创建一组具有整数值的符号常量。

使用其他消息类型

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

message SearchResponse {
  repeated Result results = 1;
}

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

导入定义

    在前面的示例中,Result消息类型是在与SearchResponse相同的文件中定义的——如果您想要用作字段类型的消息类型已经在另一个.proto文件中定义了怎么办?

    您可以通过导入其他.proto文件来使用它们。要导入另一个.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

     protocol编译器使用-I/--proto_path标志在协议编译器命令行中指定的一组目录中搜索导入的文件。如果没有给出标志,则在调用编译器的目录中查找。一般来说,应该将--proto_path标志设置为项目的根目录。

嵌套类型

    你可以在其他消息类型中定义和使用消息类型,如下面的例子所示——这里的Result消息是在SearchResponse消息中定义的:

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;
}

    你可以多层嵌套信息。在下面的例子中,请注意两个名为Inner的嵌套类型是完全独立的,因为它们是在不同的消息中定义的:

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_”,或者保留字段编号,以便将来使用.proto的用户不会意外地重用该编号导致错误。

    4. Int32、uint32、int64、uint64和bool都是兼容的,这意味着您可以将一个字段从这些类型中的一种更改为另一种,而不会破坏向前或向后兼容性。如果从字节流中解析的数字不适合相应的类型,那么您将获得与在c++中将该数字强制转换为该类型相同的效果(例如,如果将64位数字读取为int32,则将其截断为32位)。

    5. sint32和sint64相互兼容,但与其他整数类型不兼容。

    6. 只要字节是有效的UTF-8, string和bytes是兼容的。

    7.如果字节包含消息的编码版本,则嵌入式消息与字节兼容。

    8.fixed32与sfixed32兼容,fixed64与sfixed64兼容。

    9. 对于字符串、字节和消息字段,可选与repeat兼容。给定一个repeat字段的序列化数据作为输入,如果该字段是基本类型字段,希望该字段是optional的客户端将接受最后一个输入值,如果是消息类型字段,则合并所有输入元素。请注意,这对于数值类型(包括bool和enum)通常不安全。数字类型的repeat字段可以以打包格式序列化,当期望使用optional字段时,将无法正确解析该格式。

    10. enum兼容int32、uint32、int64和uint64(注意,不匹配的值会被截断)。但是,请注意,当消息被反序列化时,客户端代码可能会以不同的方式对待它们:例如,消息中将保留无法识别的proto3枚举类型,但是当消息被反序列化时,如何表示它取决于语言。

Any

    Any消息类型允许您使用消息作为嵌入类型,而不需要它们的.proto定义。Any包含任意序列化的字节消息,以及作为该消息类型的全局唯一标识符并解析为该消息类型的URL。要使用Any类型,您需要导入google/protobuf/ any .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()方法:

// Storing an arbitrary message type in Any.
NetworkErrorDetails details = ...;
ErrorStatus status;
status.add_details()->PackFrom(details);

// Reading an arbitrary message from Any.
ErrorStatus status = ...;
for (const google::protobuf::Any& detail : status.details()) {
  if (detail.Is<NetworkErrorDetails>()) {
    NetworkErrorDetails network_error;
    detail.UnpackTo(&network_error);
    ... processing network_error ...
  }
}

Oneof
    如果您的消息有许多字段,并且最多同时设置一个字段,则可以通过使用oneof特性强制执行此行为并节省内存。

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

    注意,如果设置了多个值,则由原型中的顺序决定的最后一个设定值将覆盖之前的所有设定值。

    其中一个字段的字段号在封闭消息中必须是唯一的。

使用Oneof

    要在.proto中定义oneof,你可以使用oneof关键字后跟oneof名称,在本例中为test_oneof:

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

    将oneof字段添加到oneof定义中。您可以添加任何类型的字段,除了map字段和repeat字段。如果需要向其中一个添加重复字段,可以使用包含重复字段的消息。

Oneof特性

    设置oneof字段将自动清除oneof字段的所有其他成员。因此,如果您设置了几个oneof字段,那么只有您设置的最后一个字段仍然具有值。

SampleMessage message;
message.set_name("name");
CHECK_EQ(message.name(), "name");
// Calling mutable_sub_message() will clear the name field and will set
// sub_message to a new instance of SubMessage with none of its fields set.
message.mutable_sub_message();
CHECK(message.name().empty());

    1.如果解析器在网络上遇到相同的多个成员,则只在解析的消息中使用看到的最后一个成员。

     2. oneof不能是repeated。

    如果您正在使用c++,请确保您的代码不会导致内存崩溃。下面的示例代码将崩溃,因为已经通过调用set_name()方法删除了sub_message。

SampleMessage message;
SubMessage* sub_message = message.mutable_sub_message();
message.set_name("name");      // Will delete sub_message
sub_message->set_...            // Crashes here

向后兼容性问题

    在添加或删除其中一个字段时要小心。如果检查oneof的值返回None/NOT_SET,这可能意味着oneof没有被设置,或者它已被设置为oneof的不同版本中的字段。

Maps

    如果你想创建一个关联映射作为你的数据定义的一部分,protocol buffers提供了一个方便的快捷语法:

map<key_type, value_type> map_field = N;

    其中key_type可以是任何整数或字符串类型(因此,任何标量类型,除了浮点类型和字节)。注意,enum和proto消息对key_type都无效。value_type可以是任何类型,除了另一个map。

    因此,例如,如果你想创建一个项目映射,其中每个项目消息与一个字符串键相关联,你可以这样定义它:

map<string, Project> projects = 3;

Maps的特性

    1.map字段不能是repeated。

    2.在为.proto生成文本格式时,映射按key排序,数字key按数字排序。

    3.在从连接进行解析或合并时,如果存在重复的映射键,则使用最后看到的键。从文本格式解析映射时,如果存在重复键,解析可能会失败。

    4.如果为映射字段提供键但没有值,则序列化字段时的行为依赖于语言。在c++、Java、Kotlin和Python中,该类型的默认值是序列化的,而在其他语言中则没有序列化。

    5.符号FooEntry不能存在于map foo的同一作用域中,因为FooEntry已经被map的实现使用了。

向后兼容性

    map语法在网络上相当于下面的语法,所以不支持map的protocol buffers实现仍然可以处理你的数据:

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

repeated MapFieldEntry map_field = N;

Packages

     您可以向.proto文件添加一个可选的包说明符,以防止协议消息类型之间的名称冲突。

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

    然后,您可以在定义消息类型的字段时使用包说明符:

message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

    包说明符影响生成代码的方式取决于您选择的语言:

    在c++中,生成的类被封装在一个c++命名空间中。例如,Open将位于名称空间foo::bar中。

    建议为.proto文件指定包,否则它可能导致描述符中的命名冲突,并使proto无法移植到其他语言。

enum值选项

    支持枚举值选项。您可以使用deprecated选项来指示不应该再使用某个值。

    下面的示例显示了添加这些选项的语法:

import "google/protobuf/descriptor.proto";

extend google.protobuf.EnumValueOptions {
  optional string string_name = 123456789;
}

enum Data {
  DATA_UNSPECIFIED = 0;
  DATA_SEARCH = 1 [deprecated = true];
  DATA_DISPLAY = 2 [
    (string_name) = "display_value"
  ];
}

生成类

    要生成需要处理.proto文件中定义的消息类型的Java、Kotlin、Python、c++、Go、Ruby、Objective-C或c#代码,您需要在.proto文件上运行protocol buffer编译器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

    IMPORT_PATH指定在解析导入指令时查找.proto文件的目录。如果省略,则使用当前目录。可以通过多次传递——proto_path选项来指定多个导入目录;他们将被按顺序搜查。-I=_IMPORT_PATH_可以用作--proto_path的简写形式。

    你可以提供一个或多个输出指令:

    --cpp_out在DST_DIR中生成c++代码(其他生成对应的编程语言代码)。

    如果DST_DIR以.zip或. jar结尾,编译器将把输出写到一个具有给定名称的zip格式的归档文件中。注意,如果输出存档已经存在,它将被覆盖。

    您必须提供一个或多个.proto文件作为输入。可以一次指定多个.proto文件,尽管文件是相对于当前目录命名的,但每个文件必须在在import_path之中。

文件位置

    最好不要将.proto文件放在与其他语言源文件相同的目录中。考虑在项目的根目录下为.proto文件创建子目录proto。

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

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

相关文章

Docker学习笔记(二)镜像、容器、仓库相关命令操作

一、docker镜像操作 列出镜像列表 我们可以使用 docker images 来列出本地主机上的镜像。 各个选项说明: REPOSITORY&#xff1a;表示镜像的仓库源 TAG&#xff1a;镜像的标签 IMAGE ID&#xff1a;镜像ID CREATED&#xff1a;镜像创建时间 SIZE&#xff1a;镜像大小 查…

Linux上搭建邮件服务

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 Linux上搭建邮件服务 前言电子邮件的工作原理和基本组成部分1. 电子邮件的工作原理2. 电子邮件的…

imx6ull/linux应用编程学习(7)在LCD上显示文字

在linux中&#xff0c;确实可以像裸机一样自己取模、自己写函数打点显示&#xff0c;但是效率很低&#xff0c;不能满足多文字显示&#xff0c;在Linux 系统中&#xff0c; 字体文件通常会放在/usr/share/fonts 目录下&#xff0c;有了字体文件之后&#xff0c;我们就不需要再对…

手把手edusrc漏洞挖掘和github信息收集

0x1 前言 这里主要还是介绍下新手入门edusrc漏洞挖掘以及在漏洞挖掘的过程中信息收集的部分哈&#xff01;&#xff08;主要给小白看的&#xff0c;大佬就当看个热闹了&#xff09;下面的话我将以好几个不同的方式来给大家介绍下edusrc入门的漏洞挖掘手法以及利用github信息收…

【sqlite3】联系人管理系统

SQLite3实现简单的联系人管理系统 有关sqlite3的基础知识请点击&#xff1a;SQLite3的使用 效果展示&#xff1a; 创建一个名为contacts.db的数据库 首先&#xff0c;我们需要创建一个名为contacts.db的数据库&#xff0c;并建立一个名为"contact"的表&#xff0…

20.《C语言》——【移位操作符】

&#x1f339;开场语 亲爱的读者&#xff0c;大家好&#xff01;我是一名正在学习编程的高校生。在这个博客里&#xff0c;我将和大家一起探讨编程技巧、分享实用工具&#xff0c;并交流学习心得。希望通过我的博客&#xff0c;你能学到有用的知识&#xff0c;提高自己的技能&a…

八大催化剂,花旗为何高调看涨港股?

港股三大指数今日迎来快速拉升行情&#xff0c;恒生科技指数午间一度大幅上涨2.51%&#xff0c;恒指、国指分别上涨1.18%及1.22%&#xff0c;恒指一度收复万八关口。截止收盘&#xff0c;恒生仍然保持1.18%左右的涨幅。 7月2日&#xff0c;据恒生指数公司消息&#xff0c;恒生…

Docker加速器配置指南:提升镜像下载速度的秘诀 加速安装Mysql Redis ES

在安装 Docker 镜像时&#xff0c;由于官方镜像下载速度较慢&#xff0c;我们可以使用阿里云的镜像加速器来提升下载速度。 使用阿里云镜像加速器 首先&#xff0c;找到并配置阿里云的镜像加速器。安装教程如下&#xff1a; 登录阿里云&#xff0c;进入容器镜像服务。直达链…

VSCode里python代码不扩展/级联了的解决办法

如图 解决办法&#xff1a;重新下载新的扩展工具 步骤如下 1、在左边工具栏打开Extensions 2、搜索框输入python&#xff0c;选择别的扩展工具&#xff0c;点击Install - 3在扩展工具所在的目录下&#xff0c;新建一个文件&#xff0c;就可以用了

地理信息科学:生态保护的智慧经纬

在地球这颗蓝色星球上&#xff0c;每一片森林的呼吸、每一条河流的流淌&#xff0c;都是生命交响曲中不可或缺的音符。而地理信息科学&#xff08;GIS&#xff09;&#xff0c;正是我们手中解读自然密码、护航生态平衡的精密仪器。今天&#xff0c;让我们深入探讨GIS如何在生物…

蒙阴蜜桃节:北纬 35 度的甜蜜盛宴

蒙阴&#xff0c;这座位于北纬 35 度黄金水果带的魅力之城&#xff0c;凭借着沙壤土、长日照、大温差、好生态的天然禀赋&#xff0c;孕育出了令人陶醉的“蒙阴蜜桃——北纬 35 度的甜”。 7月2日—3日&#xff0c;主题为“蒙阴好丰景 桃香产业兴”的国家鲁中山区桃产业集群项目…

Atom CMS v2.0 SQL 注入漏洞(CVE-2022-25488)

前言 CVE-2022-25488 是一个发现于 Telesquare SDT-CW3B1 设备中的命令注入漏洞。这一漏洞可以被未经认证的远程攻击者利用&#xff0c;通过特殊构造的 HTTP 请求在设备上执行任意命令。以下是关于该漏洞的详细信息&#xff1a; 漏洞详细信息 漏洞编号: CVE-2022-25488影响范…

V-bind指令配合图片轮播案例

我们先写个图片&#xff0c;这个图片是静态的&#xff0c;不会轮播改变。但有时图片是动态的&#xff0c;要通过一些程序对它进行动态改变&#xff0c;这时就需要V-bind来进行绑定&#xff0c;先放个图片进去&#xff0c;代码如下&#xff1a; <template><view>&l…

智驾追平、销量复活,蔚来又有未来了?

文&#xff5c;刘俊宏 编&#xff5c;王一粟 六月&#xff0c;是智能汽车集体“狂欢”的月份。 根据最新的汽车销量显示&#xff0c;多家智能汽车厂商“开起了香槟”。理想汽车和鸿蒙智行分列“榜一榜二”&#xff0c;两者分别以47774辆和46141辆的成绩&#xff0c;“咬的难…

探索Linux:开源世界的无限可能

Linux是一款开源操作系统&#xff0c;它的起源可以追溯到上世纪90年代初。这个故事始于一个名叫Linus Torvalds的芬兰大学生&#xff0c;他在1983年开始编写一个用于个人电脑的操作系统内核。在他的努力下&#xff0c;Linux逐渐发展成为一个稳定而强大的操作系统。 然而&#…

预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来

预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来 目录 预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.预测未来 | Matlab实现HMM隐马尔科夫时间序列预测未来 2.运行环境为Matlab2023b及以上&#xff1b; 3…

安卓手机软件自动运行插件的开发流程及代码科普!

随着智能手机的普及和移动互联网的快速发展&#xff0c;安卓手机软件的需求日益旺盛&#xff0c;为了提高软件的功能性和扩展性&#xff0c;许多开发者选择通过插件的方式为软件添加新功能。 一、安卓手机软件自动运行插件的开发流程 1、明确需求与目标 在开发安卓手机自动运…

炎黄数智人:万科集团——智能催收专员‘崔筱盼’,引领财务管理数字化转型

在数字化时代的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术的飞速发展正深刻改变着商业世界的面貌。万科集团&#xff0c;作为中国房地产行业的翘楚&#xff0c;一直致力于探索和实践最前沿的科技创新。此次&#xff0c;万科集团推出的数字员工“崔筱盼”&#…

pytorch、pytorch_lightning、torchmetrics版本对应

目录 1.pytorch_lightning对应版本安装 2.PyTorch Lightning介绍 PyTorch Lightning 的作用&#xff1a; PyTorch Lightning 的基本用法&#xff1a; 报错&#xff1a;ModuleNotFoundError: No module named pytorch_lightning 这种报错一看就是缺了pytorch_lightning包&am…