protobuf数据类型

protobuf特殊数据类型

  • 一,特殊数据类型
    • 1,enum
      • 使用规则
      • 注意事项
    • 2,Any
    • 3,oneof
    • 4,map
    • 通讯录demo升级
  • 二,默认值
  • 三,更新消息
    • 更新规则
    • 保留字段reserved
    • 未知字段
      • 获取未知字段
  • 四,选项option
    • 常见选项

一,特殊数据类型

🚀借助这些新的数据类型,来继续完善前面使用的通讯录demo。

1,enum

使用规则

🚀首先见一下枚举类型如何使用,以及编译后生成的.h文件中为我们提供了那些方法:

syntax = "proto3";enum PhoneType{MP = 0;    //移动电话TEL = 1;   //固定电话
}message Phone {string number = 1;PhoneType type = 2;
}

Phone这个消息体内包括电话号码,以及电话的类型(座机还是手机)。
🚀看看编译后的文件中为我们提供了那些关于枚举类型的方法:

protoc --cpp_out=./ test_enum.proto //使用该指令编译.proto文件

在这里插入图片描述
PhoneType_Name();函数作用是将枚举常量转化为,对应的枚举值的名称。
(例如:0 -> MP)
在这里插入图片描述

🚀使用枚举时的规则:

0值常量必须存在且必须为第一个。
枚举定义可以定义在消息体外侧,也可以定义在消息体内。(嵌套定义)
枚举常量取值范围在32位整数范围内,不建议使用负数。(负值无效,与编码规则有关)

注意事项

🚀同级的枚举常量中,各个枚举类型中常量的名称不能重名。

//在同一proto文件下,会编译报错
enum PhoneType{MP = 0;    //移动电话TEL = 1;   //固定电话
}
enum Test {TEL = 0;
}

在这里插入图片描述
🚀在同一proto文件中,外层的枚举类型和嵌套定义在消息体内的枚举类型不算同级。

enum PhoneType{MP = 0;    //移动电话TEL = 1;   //固定电话
}message Phone {string number = 1;PhoneType type = 2;enum Test {TEL = 0;}
}

🚀引入其他proto文件时,若两个proto文件都没有声明package,且两个枚举类型都在最外侧,算同级处理。

//test_enum2.proto
syntax = "proto3";
enum Test {TEL = 0;
}//test_enum.proto
syntax = "proto3";
import "test_enum2.proto";
enum PhoneType{MP = 0;    //移动电话TEL = 1;   //固定电话
}message Phone {string number = 1;PhoneType type = 2;
}

同样会编译报错。
在这里插入图片描述
🚀若引入其他proto文件时,枚举类型定义在package内部,那么两个枚举类型不算同级。

//test_enum2.proto
syntax = "proto3";
package test2;
enum Test {TEL = 0;
}

加上package后就不会编译报错。

2,Any

🚀字段声明为Any类型,可以理解为泛型类型。在使用时。Any中可以存储任意类型的消息类型。同时Any类型可以用repeated修饰。
Any类型是google已经帮我们定义好的类型,可以在protobuf的安装路径下的include路ing下查看。
在这里插入图片描述
在使用时引入 “google/protobuf/any.proto”文件即可。并且使用时要指明其命名空间。

syntax = "proto3";
import "google/protobuf/any.proto";message Test {string name = 1;google.protobuf.Any msg = 2;
}

🚀看下编译后生成了哪些它用于操作Any类型的方法。
在这里插入图片描述
mutable函数返回的是指向该类型的指针变量,这类方法以为我们开辟好空间,我们可以通过指针来对这块空间操作。

🚀Any类型可以存储任意类型的消息,这就要设计任意类型和Any类型之间的转换,any.pb.h已经为我们实现了这些方法。
在这里插入图片描述
在这里插入图片描述
🚀PackFrom方法是将别的消息类型转化为Any类型,UnpackTo方法是将Any类型转化为特定的消息类型。

在这里插入图片描述
🚀any类型中还带了一个Is函数,其作用是判断any中存储的消息类型是否与模板类型一致。

3,oneof

syntax = "proto3";
message PeopleInfo {string name  = 1;string phone_number = 2;oneof other_contact {string qq = 3;string wechat = 4;}
}

🚀oneof 顾名思义:多选一,如果消息中声明了很多字段,但最终只会有一个字段会被设置,那么就可以用oneof来完成这个效果。上面的other_contact只能被设置为qq 和 微信中的一个。

在这里插入图片描述
🚀对于qq,wechat除了提供get/set方法外,还提供了一个_case();的接口,
首先,编译器会将oneof中的多个字段名定义为枚举类型,_case函数返回的就是一个枚举类型表示设置了哪一个字段。
在这里插入图片描述
注意:如果对oneof字段设置了多次,那么只会保留最后一次设置的字段。

4,map

🚀protobuf中也支持map这种类型,注意的是map中key-type可以是除了float和bytes类型之外的所有标量类型。value-type可以是任意类型。
注意: map类型不能被repeated修饰。并且插入到map中的数据是无序的。

syntax = "proto3";message PeopleInfo {string name = 1;map<string,string> remark = 2;
}

在这里插入图片描述

map类型的set放法也是返回一块已开辟的空间的地址,用户通过指针去操控这块空间。

通讯录demo升级

contacts.proto

syntax = "proto3";
package contacts;
import "google/protobuf/any.proto";
message Address {string home_address = 1;string uint_address = 2;
}
message PeopleInfo {string name = 1;       //姓名int32 age = 2;         //年龄enum PhoneType {MP = 0;TEL = 1;}message Phone {string number = 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 Contact {repeated PeopleInfo people = 1;
}

write.cc

#include <iostream>
#include <fstream>
#include <string>
#include "contacts.pb.h"using namespace std;void AddPerson(contacts::PeopleInfo* p) {cout << "------------添加联系人------------" << endl;cout << "------联系人姓名: ";string name;getline(cin,name);p->set_name(name);cout << "------联系人年龄: ";int age;cin >> age;cin.ignore(256,'\n'); //清除缓冲区内的'\n'p->set_age(age);for (int i = 0; ; i++) {cout << "联系人第" << i + 1 << "个电话(只输入空格结束): ";string number;getline(cin,number);if (number.empty()) {break;}contacts::PeopleInfo_Phone* phone = p->add_phone();phone->set_number(number);cout << "请输入电话类型(1.移动电话 2.固定电话): ";int type;cin >> type;cin.ignore(256,'\n');switch (type) {case 1 :phone->set_type(contacts::PeopleInfo_PhoneType::PeopleInfo_PhoneType_MP);break;case 2 :phone->set_type(contacts::PeopleInfo_PhoneType::PeopleInfo_PhoneType_TEL);break;default:cout << "输入有误!!!" << endl;break;}}contacts::Address address;cout << "------请输入家庭地址: ";string home_address;getline(cin,home_address);address.set_home_address(home_address);cout << "------请输入工作地址: ";string unit_address;getline(cin,unit_address);address.set_uint_address(unit_address);google::protobuf::Any* data = p->mutable_data();data->PackFrom(address);cout << "请输入其他联系方式(1.qq 2.微信): ";int other;cin >> other;cin.ignore(256,'\n');switch (other) {case 1 :{string qq;cout << "------用户qq号码: ";getline(cin,qq);p->set_qq(qq);break;}case 2 :{string wechat;cout << "------用户微信号码: ";getline(cin,wechat);p->set_wechat(wechat);break;}default:cout << "输入有误!!!" << endl;break;}google::protobuf::Map< std::string, std::string >* map = p->mutable_remark();for (int i = 0; ; i++) {cout << "请输入第" << i + 1 << "个备注标题(输入空格结束): ";string key;getline(cin,key);if (key.empty()) {break;}string value;cout << "请输入第" << i + 1 << "个备注内容: ";getline(cin,key);map->insert({key,value});}cout << "-----------成功添加联系人-----------" << endl;
}
int main() {contacts::Contact contact;//1.打开通讯录文件将文件的内容反序列化到内存中fstream input("./contacts.bin",ios::in | ios::binary);if (!input) {cout << "contacts.bin not exist,create it!!!" << endl;} else if (!contact.ParseFromIstream(&input)) {cout << "prase error" << endl;input.close();return -1;}fstream output("./contacts.bin",ios::out | ios::binary | ios::trunc);AddPerson(contact.add_people());//序列化写入文件if (!contact.SerializeToOstream(&output)) {cout << "Serialize error" << endl;input.close();output.close();return -1;}cout << "write sucess!!!" << endl;input.close();output.close();google::protobuf::ShutdownProtobufLibrary();return 0;
}

read.cc

#include <iostream>
#include <fstream>
#include <string>
#include "contacts.pb.h"using namespace std;void ReadContact(const contacts::Contact& contact) {for (int i = 0; i < contact.people_size(); i++) {const ::contacts::PeopleInfo& person = contact.people(i);cout << "------联系人" << i + 1 << "------" << endl;cout << "姓名: " << person.name() << endl;cout << "年龄: " << person.age() << endl;//电话信息for (int j = 0; j < person.phone_size(); j++) {const ::contacts::PeopleInfo_Phone& phone = person.phone(j);cout << "第" << j + 1 << "电话号码: " << phone.number()<< "     [" << contacts::PeopleInfo::PhoneType_Name(phone.type()) << "]" << endl; }if (person.has_data() && person.data().Is<contacts::Address>()) {contacts::Address address;person.data().UnpackTo(&address);cout << "家庭住址: " << address.home_address() << endl;cout << "工作地址: " << address.uint_address() << endl;}switch (person.other_contact_case()) {case contacts::PeopleInfo::kQq :cout << "qq号码: " << person.qq() << endl;break;case contacts::PeopleInfo::kWechat :cout << "微信号码: " << person.wechat() << endl;default:break;}if (person.remark().size()) {auto it = person.remark().begin();while (it != person.remark().end()) {cout << "备注信息: " << endl<< "          " << it->first << ":" << it->second << endl;++it;}}}
}
int main (){fstream input("contacts.bin",ios::in | ios::binary);//1.从文件中反序列化读取通讯录信息contacts::Contact contact;if (!contact.ParseFromIstream(&input)) {cout << "prase from file error" << endl;}ReadContact(contact);return 0;
}

二,默认值

🚀反序列化消息时,如果被反序列化的二进制序列中不包含某个字段,反序列化对象中有相应字段,该字段就会被设置成该字段对应的默认值。不同类型对应的默认值是不同的:

  • 对于字符串,默认值为空字符串。
  • 对于字节,默认值为空字节。
  • 对于布尔值,默认值为false。
  • 对于数值类型,默认值为0。(浮点型就是0.0)
  • 对于枚举,默认值是第一个枚举常量,必须为0。
  • 对于消息字段,未设置该字段。它的取值依赖于语言。
  • 对于设置了repeated的字段,默认值是空的。
  • 对于消息类型,oneof字段,和any字段,C++和Java语言中都有has_方法来检测当前字段是否被设置。

三,更新消息

🚀假设通讯录中只有三个字段,姓名,年龄和电话。在写端将年龄字段修改为了生日,但是在读端并没有做出相应的修改,在添加新的联系人时会发生什么现象呢?

//修改之前的proto文件,读端和写段是一致的
message PeopleInfo{string name = 1;int32 age = 2;string Phone = 3;
}
message Contact{repeated PeopleInfo people = 1;
}
//修改之后,写端的proto文件做了以下修改
message PeopleInfo{string name = 1;int32 birthday = 2;  //修改项string Phone = 3;
}
message Contact{repeated PeopleInfo people = 1;
}

在这里插入图片描述
🚀很显然出了一个问题,写端的生日字段在反序列化的时候被解释成了年龄字段,这是因为我们并没有遵循更新消息的规则,而导致的问题。

更新规则

🚀更新消息时注意以下几点:

  • 禁⽌修改任何已有字段的字段编号。
  • 若是移除⽼字段,要保证不再使⽤移除字段的字段编号。正确的做法是保留字段编号(reserved),以确保该编号将不能被重复使⽤。不建议直接删除或注释掉字段。
  • int32, uint32, int64, uint64 和 bool 是完全兼容的。可以从这些类型中的⼀个改为另⼀个,⽽不破坏前后兼容性。若解析出来的数值与相应的类型不匹配,会采⽤与 C++ ⼀致的处理⽅案(例如,若将 64 位整数当做 32 位进⾏读取,它将被截断为 32 位)。
  • sint32 和 sint64 相互兼容但不与其他的整型兼容。
  • string 和 bytes 在合法 UTF-8 字节前提下也是兼容的。
  • bytes 包含消息编码版本的情况下,嵌套消息与 bytes 也是兼容的。
  • fixed32 与 sfixed32 兼容, fixed64 与 sfixed64兼容。
  • enum 与 int32,uint32, int64 和 uint64 兼容(注意若值不匹配会被截断)。但要注意当反序列化消息时会根据语⾔采⽤不同的处理⽅案:例如,未识别的 proto3 枚举类型会被保存在消息中,但是当消息反序列化时如何表⽰是依赖于编程语⾔的。整型字段总是会保持其的值。
  • oneof:
    ◦ 将⼀个单独的值更改为 新 oneof 类型成员之⼀是安全和⼆进制兼容的。
    ◦ 若确定没有代码⼀次性设置多个值那么将多个字段移⼊⼀个新 oneof 类型也是可⾏的。
    ◦ 将任何字段移⼊已存在的 oneof 类型是不安全的。

保留字段reserved

🚀如果通过 删除 或 注释掉 字段来更新消息类型,未来的⽤⼾在添加新字段时,有可能会使⽤以前已经存在,但已经被删除或注释掉的字段编号。将来使⽤该 .proto 的旧版本时的程序会引发很多问题:数据损坏、隐私错误等等。
🚀为了防止这一现象发生,可以使用reserved关键字,对之前的字段编号或者名称进行保留,当我们再使用这些编号或者名称的时候,编译器会报错。

message PeopleInfo{reserved 2 , 7, 100 to 200; //可以一次保留多个字段编号用逗号隔开reserved "age";             //也可以像 100 to 200这样使用,表示保留的100到200的字段编号string name = 1;            //也可以一次保留多个字段名称用逗号隔开即可。//int32 age = 2;int32 birthday = 4; string Phone = 3;
}

未知字段

🚀在上面我们更新了写段的proto文件,对于读端的proto文件没有修改,并且我们对旧字段的名称和编号做了reserved处理,这样就意味着序列化的二进制文件中存在生日这个字段,但是在读端反序列化后的对象中不存在生日的字段,这时候这个生日字段就会被当作未知字段来处理。

  • 未知字段:解析结构良好的 protocol buffer 已序列化数据中的未识别字段的表⽰⽅式。例如,当旧程序解析带有新字段的数据时,这些新字段就会成为旧程序的未知字段。
  • 本来,proto3 在解析消息时总是会丢弃未知字段,但在 3.5 版本中重新引⼊了对未知字段的保留机制。所以在 3.5 或更⾼版本中,未知字段在反序列化时会被保留,同时也会包含在序列化的结果中。

获取未知字段

在这里插入图片描述

🚀获取未知字段的流程:GetReflection();—>GetUnknownfields();---->field();

在这里插入图片描述
🚀UnknowField类中,提供了获取未知字段的字段编号,字段的值,字段类型等相关方法。并且对字段类型进行了划分也就是上图中的枚举类型。

🚀对于上面使用reserved保留字段的名称和字段编号后,再次添加联系人的时候,在由于读端仍旧是老版本的proto文件,那么对于生日字段对于其就是未知字段。

const google::protobuf::Reflection *reflection = person.GetReflection();
const google::protobuf::UnknownFieldSet &set = reflection->GetUnknownFields(person);
for (int j = 0; j < set.field_count(); j++)
{const google::protobuf::UnknownField &f = set.field(j);cout << "未知字段: " << j + 1 << " :" << endl;cout << "字段编号: " << f.number()<< "类型: " << f.type();switch (f.type()){case google::protobuf::UnknownField::Type::TYPE_VARINT:cout << " 值: " << f.varint() << endl;break;case google::protobuf::UnknownField::Type::TYPE_LENGTH_DELIMITED:cout << " 值: " << f.length_delimited() << endl;break;// case ...}
}

在这里插入图片描述

四,选项option

🚀.proto ⽂件中可以声明许多选项,使⽤ option 标注。选项能影响 proto 编译器的某些处理⽅式。选项的完整列表在google/protobuf/descriptor.proto中定义。

常见选项

option optimize_for = LITE_RUNTIME;

• optimize_for : 该选项为⽂件选项,可以设置 protoc 编译器的优化级别,分别为 SPEED 、CODE_SIZE 、 LITE_RUNTIME 。受该选项影响,设置不同的优化级别,编译 .proto ⽂件后生成的代码内容不同。

◦ SPEED : protoc 编译器将⽣成的代码是⾼度优化的,代码运⾏效率⾼,但是由此⽣成的代码编译后会占⽤更多的空间。 SPEED 是默认选项。

◦ CODE_SIZE : proto 编译器将⽣成最少的类,会占⽤更少的空间,是依赖基于反射的代码来实现序列化、反序列化和各种其他操作。但和 SPEED 恰恰相反,它的代码运⾏效率较低。这种⽅式适合⽤在包含⼤量的.proto⽂件,但并不盲⽬追求速度的应⽤中。

◦ LITE_RUNTIME : ⽣成的代码执⾏效率⾼,同时⽣成代码编译后的所占⽤的空间也是⾮常少。这是以牺牲Protocol Buffer提供的反射功能为代价的,仅仅提供 encoding+序列化 功能,所以我们在链接 BP 库时仅需链接libprotobuf-lite,⽽⾮libprotobuf。这种模式通常⽤于资源有限的平台,例如移动⼿机平台中。

enum PhoneType {option allow_alias = true;MP = 0;TEL = 1;LANDLINE = 1; // 若不加 option allow_alias = true; 这⼀⾏会编译报错
}

🚀allow_alias : 允许将相同的常量值分配给不同的枚举常量,⽤来定义别名。该选项为枚举选项。

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

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

相关文章

时频分析方法的matlab实现

傅里叶变换 function [ output_args ] example3_7( input_args ) %EXAMPLE3_7 Summary of this function goes here % Detailed explanation goes here clc; clear; fs12800;%采样频率 s1load(Sig1.txt); s2load(Sig2.txt); lslength(s1); figure(1) subplot(211) plot…

Cilium 系列-7-Cilium 的 NodePort 实现从 SNAT 改为 DSR

系列文章 Cilium 系列文章 前言 将 Kubernetes 的 CNI 从其他组件切换为 Cilium, 已经可以有效地提升网络的性能。但是通过对 Cilium 不同模式的切换/功能的启用&#xff0c;可以进一步提升 Cilium 的网络性能。具体调优项包括不限于&#xff1a; 启用本地路由 (Native Rou…

西安市未央区地方财政支出绩效管理研究_kaic

摘 要 目前传统的地方财政绩效管理研究普遍上主要集中在有关收入研究方面上&#xff0c;而对其支出的规模以及各类结构的研究较少。我国大部分地方财政政府的财政收入低下&#xff0c;财政支出效率有限&#xff0c;不能很好的为其地方经济提供较为稳定的社会支撑和经济保障。造…

SQL基础使用

SQL的概述 SQL全称&#xff1a; Structured Query Language&#xff0c;结构化查询语言&#xff0c;用于访问和处理数据库的标准的计算机语言。 SQL语言1974年由Boyce和Chamberlin提出&#xff0c;并首先在IBM公司研制的关系数据库系统SystemR上实现。 经过多年发…

PostgreSQL构建时间

– PostgreSQL构建时间 select make_timestamp(2023,7,27,7,34,16);

K8s-资源管理(二)

文章目录 2. 资源管理2.1 资源管理介绍2.2 YAML语言介绍2.3 资源管理方式2.3.1 命令式对象管理2.3.2 命令式对象配置2.3.3 声明式对象配置 2.4. 模拟使用普通用户来操作2.5 kubectl 一些基本命令2.6 使用个人的 docker 仓库的镜像 2. 资源管理 2.1 资源管理介绍 在kubernetes…

基于深度学习的高精度狗狗检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度狗狗检测识别系统可用于日常生活中检测与定位120类狗狗目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的狗狗目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型训练…

web漏洞-java安全(41)

这个重点是讲关于java的代码审计&#xff0c;看这些漏洞是怎么在java代码里面产生的。 #Javaweb 代码分析-目录遍历安全问题 这个漏洞原因前面文章有&#xff0c;这次我们看看这个漏洞如何在代码中产生的&#xff0c;打开靶场 解题思路就是通过文件上传&#xff0c;上传文件…

如何利用tf.keras 实现深度学习?

tf.keras是TensorFlow 2.0的高阶API接口&#xff0c;为TensorFlow的代码提供了新的风格和设计模式&#xff0c;大大提升了TF代码的简洁性和复用性&#xff0c;官方也推荐使用tf.keras来进行模型设计和开发。 常用模块 tf.keras中常用模块如下表所示&#xff1a; 常用方法 深度…

安全学习DAY10_HTTP数据包

HTTP数据包 文章目录 HTTP数据包小节导图Request请求数据包结构Request请求方法&#xff08;方式&#xff09;请求头&#xff08;Header&#xff09;Response响应数据包结构Response响应数据包状态码状态码作用&#xff1a;部分状态码详解判断网站文件是否存在的状态码&#xf…

配置代理——解决跨域问题(详解)

之前写项目的时候总会遇到配置代理的问题&#xff0c;可是配置了之后有时有用&#xff0c;有时就没有用&#xff0c;自己之前学的也是懵懵懂懂&#xff0c;于是专门花了一个小时去了解了如何配置代理跨域&#xff0c;然后在此记录一下&#xff0c;方便自己以后查阅。 一、 常用…

RBF神经网络原理和matlab实现

1.案例背景 1.1 RBF神经网络概述 径向基函数(Radical Basis Function,RBF)是多维空间插值的传统技术,由Powell于1985年提出。1988年, Broomhead和 Lowe根据生物神经元具有局部响应这一特点,将 RBF引入神经网络设计中,产生了RBF神经网络。1989 年&#xff0c;Jackson论证了…

应用层协议——https

文章目录 1. HTTPS 是什么2. 什么是"加密"3. 常见的加密方式4. 数据摘要 && 数字签名5. HTTPS 的工作过程探究5.1 方案1 - 只使用对称加密5.2 方案2 - 只使用非对称加密5.3 方案3 - 双方都使用非对称加密5.4 方案4 - 非对称加密 对称加密5.5 中间人攻击5.6 …

【深度学习】InST,Inversion-Based Style Transfer with Diffusion Models,论文

代码&#xff1a;https://github.com/zyxElsa/InST 论文&#xff1a;https://arxiv.org/abs/2211.13203 文章目录 AbstractIntroductionRelated WorkImage style transferText-to-image synthesisInversion of diffusion models MethodOverview ExperimentsComparison with Sty…

C++ 多进程学习总结

C多进程 进程间通信 消息队列 消息队列&#xff1a;提供一个种进程间发送/接收数据块&#xff08;常为结构体数据&#xff09;的方法。 函数接口 ftok()&#xff1a;获取消息队列键值msgget()&#xff1a;创建和访问消息队列msgsnd()&#xff1a;向消息队列发送数据msgrcv…

VSCode配置之C++ SQLite3极简配置方案

背景 最近在学习《深入应用C11: 代码优化与工程级应用》&#xff0c;其中第13章说到SQLite库&#xff0c;查询网上诸多教程&#xff0c;发现比较容易出现bug且配置较为麻烦&#xff0c;故记录此次简化版方案&#xff0c;以供参考。 软件环境 SQLite 3.42.0 版本&#xff08;仅…

Python 进阶(五):os 模块

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 1. 文件和目录的基本操作1.1 获取当前工作目录1.2 更改当前工作目录1.3 获取目录下所有…

ffmpeg批量分割视频解决视频前几秒黑屏的问题解决

echo 请输入视频地址&#xff1a; set /p fp echo 请输入开始时间&#xff1a; set /p st echo 请输入结束时间&#xff1a; set /p et echo 请输入分片时间&#xff1a; set /p sgt echo 注意&#xff1a;循环范围参数要空格。 for /l %%i in (%st%, %sgt%, %et%) do call :aa…

redis启动失败,oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo

在redis文件夹下&#xff0c;启动redis正常。 但是加入到system后启动redis失败。 一直处于starting状态。 对比正常redis服务的配置之后&#xff0c;把redis.conf里的守护进程关掉就可以了&#xff08;但是没用system管理之前&#xff0c;直接./redis.server启动是可以的&…