当前位置: 首页 > 编程日记 > 正文

【C++】Google Protocol Buffer(protobuf)详解(二)

代码走读:caffe中protobuf的详细使用过程

【一】proto文件,以caffe.proto中BlobShape为例

syntax = "proto2";	//指明protobuf版本,默认是v2,其它版本:"proto3"package caffe; 	// 最终生成c++代码: namespace caffe message BlobShape {	// 最终生成c++代码: class BlobShape : public ::google::protobuf::Message {//下面语句最终生成c++代码: google::protobuf::RepeatedField< ::google::protobuf::int64 > dim_;repeated int64 dim = 1 [packed = true];	//repeated列表字段,等号后面数字表示标识符,[packed=true]保证更高效的编码
}

【二】编译命令 protoc -I=. --cpp_out=. caffe.proto

【三】生成的caffe.pb.h

......
namespace caffe {	//对应caffe.proto中 package caffe;
// protobuf内部实现细节——不要调用这些接口
void  protobuf_AddDesc_caffe_2eproto();
void protobuf_AssignDesc_caffe_2eproto();
void protobuf_ShutdownFile_caffe_2eproto();class BlobShape;
// ===================================================================
class BlobShape : public ::google::protobuf::Message {	//对应caffe.proto中 message BlobShapepublic:BlobShape(); // 构造函数virtual ~BlobShape(); //析构函数BlobShape(const BlobShape& from); // 拷贝构造函数inline BlobShape& operator=(const BlobShape& from) { //赋值构造函数CopyFrom(from);return *this;}// ** UnknownFieldSet用于跟踪解析协议消息但其字段编号或类型无法识别时看到的字段。
// 这种情况最常发生在将新字段添加到消息类型中,然后包含这些字段的消息由添加新类型之前编译的旧软件读取。
// 大多数用户永远不需要使用这个类。比如在caffe中就没有使用到
// 参考网址:https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/UnknownFieldSetinline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {return _unknown_fields_;}inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {return &_unknown_fields_;}// ** 描述元数据,主要用在反射上,caffe中没有用到
// 参考网址:http://www.blogjava.net/DLevin/archive/2015/04/01/424012.htmlstatic const ::google::protobuf::Descriptor* descriptor();static const BlobShape& default_instance();void Swap(BlobShape* other);	// 交换// implements Message 消息实现----------------------------------------------BlobShape* New() const;	// 新建void CopyFrom(const ::google::protobuf::Message& from);	// 复制void MergeFrom(const ::google::protobuf::Message& from);	// 合并:单数字段会被覆盖、Repeated (类似链表)字段会被连接到一起void CopyFrom(const BlobShape& from);void MergeFrom(const BlobShape& 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;	//序列化到output中int GetCachedSize() const { return _cached_size_; }	//返回上次调用ByteSize()的结果private:void SharedCtor();void SharedDtor();void SetCachedSize(int size) const;public:::google::protobuf::Metadata GetMetadata() const;	// 获取缓冲大小// nested types message内嵌的类型 ----------------------------------------------------// 如果在proto的mesage中定义/* enum SnapshotFormat {HDF5 = 0;BINARYPROTO = 1;}*/// 则生成如下代码:/*enum SolverParameter_SnapshotFormat {SolverParameter_SnapshotFormat_HDF5 = 0,SolverParameter_SnapshotFormat_BINARYPROTO = 1};bool SolverParameter_SnapshotFormat_IsValid(int value);const SolverParameter_SnapshotFormat SolverParameter_SnapshotFormat_SnapshotFormat_MIN = SolverParameter_SnapshotFormat_HDF5;const SolverParameter_SnapshotFormat SolverParameter_SnapshotFormat_SnapshotFormat_MAX = SolverParameter_SnapshotFormat_BINARYPROTO;const int SolverParameter_SnapshotFormat_SnapshotFormat_ARRAYSIZE = SolverParameter_SnapshotFormat_SnapshotFormat_MAX + 1;typedef SolverParameter_SnapshotFormat SnapshotFormat;static const SnapshotFormat HDF5 = SolverParameter_SnapshotFormat_HDF5;static const SnapshotFormat BINARYPROTO = SolverParameter_SnapshotFormat_BINARYPROTO;static inline bool SnapshotFormat_IsValid(int value) {return SolverParameter_SnapshotFormat_IsValid(value);}static const SnapshotFormat SnapshotFormat_MIN = SolverParameter_SnapshotFormat_SnapshotFormat_MIN;static const SnapshotFormat SnapshotFormat_MAX = SolverParameter_SnapshotFormat_SnapshotFormat_MAX;static const int SnapshotFormat_ARRAYSIZE = SolverParameter_SnapshotFormat_SnapshotFormat_ARRAYSIZE;static inline const ::google::protobuf::EnumDescriptor*SnapshotFormat_descriptor() {return SolverParameter_SnapshotFormat_descriptor();}static inline const ::std::string& SnapshotFormat_Name(SnapshotFormat value) {return SolverParameter_SnapshotFormat_Name(value);}static inline bool SnapshotFormat_Parse(const ::std::string& name, SnapshotFormat* value) {return SolverParameter_SnapshotFormat_Parse(name, value);}*/// accessors -------------------------------------------------------// repeated int64 dim = 1 [packed = true]; // 对 dim 成员的操作inline int dim_size() const;	// 返回dim 列表大小inline void clear_dim();	// 清空static const int kDimFieldNumber = 1;	// *FieldNumber 的值对应 dim = 1 标识1inline ::google::protobuf::int64 dim(int index) const;	// 获取列表dim中指定索引的值inline void set_dim(int index, ::google::protobuf::int64 value); // 设置列表dim指定索引的值inline void add_dim(::google::protobuf::int64 value);	// 添加新元素到列表dim中inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >& dim() const; // 返回dim的const引用,不可修改inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*  mutable_dim();	// 返回dim的指针,可修改// @@protoc_insertion_point(class_scope:caffe.BlobShape)private:::google::protobuf::UnknownFieldSet _unknown_fields_;::google::protobuf::RepeatedField< ::google::protobuf::int64 > dim_;mutable int _dim_cached_byte_size_;mutable int _cached_size_;::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];	// 1表示 message BlobShape中定义的成员个数为1friend void  protobuf_AddDesc_caffe_2eproto();friend void protobuf_AssignDesc_caffe_2eproto();friend void protobuf_ShutdownFile_caffe_2eproto();void InitAsDefaultInstance();static BlobShape* default_instance_;
};
// ===================================================================
// BlobShape对应的操作
// repeated int64 dim = 1 [packed = true];
inline int BlobShape::dim_size() const { //dim是列表形式,因此提供返回大小的接口return dim_.size();
}
inline void BlobShape::clear_dim() { // 清空dim_.Clear();
}
inline ::google::protobuf::int64 BlobShape::dim(int index) const { //获取指定索引的值return dim_.Get(index);
}
inline void BlobShape::set_dim(int index, ::google::protobuf::int64 value) { //设置指定索引的值dim_.Set(index, value);
}
inline void BlobShape::add_dim(::google::protobuf::int64 value) { //增量添加值到列表dim中dim_.Add(value);
}
inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
BlobShape::dim() const { //返回只读dimreturn dim_;
}
inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
BlobShape::mutable_dim() { //返回可读写dimreturn &dim_;
}
......

【四】生成的caffe.pb.cc

【五】官网说明

谷歌官网说明:https://developers.google.com/protocol-buffers/
c++API手册:https://developers.google.com/protocol-buffers/docs/reference/cpp/#google.protobuf

【六】使用

1、将文本格式的 protobuf 读取到 Message 中
1.1 以 caffe 中 ReadProtoFromTextFile 为例

ReadProtoFromTextFile的调用步骤:
train() --> caffe::ReadSolverParamsFromTextFileOrDie(FLAGS_solver, &solver_param); --> ReadProtoFromTextFile(param_file, param)

1.2、ReadProtoFromTextFile 源码详解
bool ReadProtoFromTextFile(const char* filename, Message* proto) {int fd = open(filename, O_RDONLY);CHECK_NE(fd, -1) << "File not found: " << filename;FileInputStream* input = new FileInputStream(fd);bool success = google::protobuf::TextFormat::Parse(input, proto);delete input;close(fd);return success;
}

1.2.1 FileInputStream 详解
官方说明:

FileInputStream 继承自 ZeroCopyInputStream(从文件描述符读取流)

使用方法:(参考构建函数)

explicit FileInputStream(int file_descriptor, int block_size = -1);

参数:

	file_descriptor : Unix 文件描述符block_size:指定 Next() 时读取和返回的大小,不指定时,将使用一个合理的默认值。

1.2.2 google::protobuf::TextFormat::Parse
TextFormat 类:

该类实现 protobuf 文本格式。以文本格式打印和解析协议消息对于调试和人工编辑消息非常有用。
这个类实际上是一个只包含静态方法的命名空间。

静态函数 Parse:

从给定的输入流解析 文本格式协议消息 到给定的 消息对象。这个函数解析Print()编写的格式。
static bool Parse(io::ZeroCopyInputStream* input, Message* output);

其它类似静态函数:

static bool ParseFromString(const string& input, Message* output);  // 直接从字符串解析
static bool Merge(io::ZeroCopyInputStream* input, Message* output); // 从流中解析,并合并到 Message 中,参见 Message::MergeFrom()
static bool MergeFromString(const string& input, Message* output);  // 同上,但是直接从字符串解析//解析单个字段值到指定的 message 的指定 field 字段中
static bool ParseFieldValueFromString(const string& input, const FieldDescriptor* field, Message* message); 

对应的反操作:将 Message 打印到流或文本中

static bool Print(const Message& message, io::ZeroCopyOutputStream* output); // 将 message 打印到输出流中
static bool PrintToString(const Message& message, string* output);	// 将 message 打印到字符串中// 将 message 指定的 field 字段,输出到字符串中。字段是列表格式时,指定引索 index;非列表格式时 index 必须指定为 -1
static void PrintFieldValueToString(const Message& message, const FieldDescriptor* field, int index, string* output); // 打印未知数集 UnknownFieldSet 中的字段。它们只按标签号打印。通过尝试解析嵌入的消息,可以直观地识别它们。
static bool PrintUnknownFields(const UnknownFieldSet& unknown_fields, io::ZeroCopyOutputStream* output); 
2、将 message 序列化到二进制文件中
2.1 以 caffe 中 WriteProtoToBinaryFile 函数为例
void WriteProtoToBinaryFile(const Message& proto, const char* filename) {fstream output(filename, ios::out | ios::trunc | ios::binary);CHECK(proto.SerializeToOstream(&output));	// 原型bool Message::SerializeToOstream(ostream* output) const; 将 Message 序列化到c++标准输出流中,必须设置所有必需字段。
}
2.2 序列化函数汇总:
// 将 Message 序列化到文件描述符中,必须设置所有必需字段。
bool SerializeToFileDescriptor(int file_descriptor) const;	// 同上,但是允许缺少必需的字段。	
bool SerializePartialToFileDescriptor(int file_descriptor) const;// 将 Message 序列化到c++标准输出流中,必须设置所有必需字段。
bool SerializeToOstream(ostream* output) const;	// 同上,但是允许缺少必需的字段。		
bool SerializePartialToOstream(ostream* output) const; 	
3、从二进制文件中 反序列化 到 message 中
3.1 以 caffe 中 ReadProtoFromBinaryFile 为例
bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {int fd = open(filename, O_RDONLY);CHECK_NE(fd, -1) << "File not found: " << filename;ZeroCopyInputStream* raw_input = new FileInputStream(fd);CodedInputStream* coded_input = new CodedInputStream(raw_input);coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);bool success = proto->ParseFromCodedStream(coded_input);	// 原型 bool ParseFromCodedStream(io::CodedInputStream* input);delete coded_input;delete raw_input;close(fd);return success;
}
3.2 其它反序列化函数:
bool ParsePartialFromCodedStream(io::CodedInputStream* input);	// 同 ParseFromCodedStream ,但是接受缺少必需字段的消息。
bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);		// 从 io::ZeroCopyInputStream 流中获取
bool ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input); // 同上,但是接受缺少必需字段的消息。
bool ParseFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, int size); // 同上,指定 message 大小必须是 size
bool ParsePartialFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, , int size); // 同上,但是接受缺少必需字段的消息。

相关文章:

Linux使用

软件操作 软件包管理 yum安装 yum install ...卸载 yum remove ...搜索 yum serach ...清理缓存 yum clean packages列出已安装 yum list软件包信息 yum info ...硬件资源信息 内存free -m 硬盘df -h 负载&#xff08;w或top&#xff09;w 12:53:49 up 2:33, 3 users, load ave…

通过进程ID获得该进程主窗口的句柄

一个进程可以拥有很多主窗口&#xff0c;也可以不拥有主窗口&#xff0c;所以这样的函数是不存在的&#xff0c;所幸的是&#xff0c;相反的函数是有的。所以我们可以调用EnumWindows来判断所有的窗口是否属于这个进程。 typedef struct tagWNDINFO{  DWORD dwProcessId;  …

【AI】caffe源码分析(一)

【一】caffe依赖开源库 【C】google gflags详解 【C】google glog详解 【C】Google Protocol Buffer&#xff08;protobuf&#xff09;详解&#xff08;一&#xff09; 【C】Google Protocol Buffer&#xff08;protobuf&#xff09;详解&#xff08;二&#xff09; 【C】goog…

专访博世王红星:大数据和AI将是中国制造业升级新动力

数据分析挖掘与工业大数据是智能制造与工业互联网的核心&#xff0c;其本质是通过促进数据的自动流动与智能决策去解决控制和业务问题&#xff0c;有效减少决策过程所带来的不确定性&#xff0c;并尽量克服人工决策的缺点&#xff0c;从而推动智能制造进程与智能工厂的建设&…

C进阶 - 内存四驱模型

一.内存四驱模型 不知我们是否有读过 《深入理解 java 虚拟机》这本书&#xff0c;强烈推荐读一下。在 java 中我们将运行时数据&#xff0c;分为五个区域分别是&#xff1a;程序计数器&#xff0c;java 虚拟机栈&#xff0c;本地方法栈&#xff0c;java 堆&#xff0c;方法区。…

ATEN—第十章OSPF的高级配置(4)

实验使用的工具&#xff1a;小凡模拟器一、在路由器R1上&#xff0c;配置接口&#xff0c;启动ospf路由进程和rip,宣告网段&#xff0c;并配置路由重分发★☆R1☆★☆→Router>Router>enableRouter#config terminalRouter(config)#hostname R1-jinR1-jin(config)#interfa…

【ubuntu】ubuntu14.04、16.04、18.04 LTS版本支持时间

0、历史版本下载地址 http://old-releases.ubuntu.com/releases/ http://mirrors.163.com/ubuntu-releases/ 1、官网说明 https://wiki.ubuntu.com/Kernel/LTSEnablementStack 2、简要记录 如下图&#xff1a; 14.04.0(v3.13) 14.04.1(v3.13) 14.04.5(v4.4) LTS 支持至 20…

BERT拿下最佳长论文奖!NAACL 2019最佳论文奖公布

点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」全日程揭晓&#xff0c;请扫码咨询 ↑↑↑作者 | 刘静编辑 | 李尔客本文经授权转自公众号图灵Topia&#xff08;ID&#xff1a;turingtopia&#xff09;今日&#xff0c;自然语言处理顶会NAACL 2019最佳论文奖公布&#xff…

Git Bash修改默认路径

Git Bash默认安装在C:/user目录下&#xff0c;如果管理其他目录的代码库&#xff0c;需要切换目录。 修改Git Bash的默认路径&#xff0c;不需要每次切换了。 方法&#xff1a; 桌面Git Bash快捷方式&#xff0c;右键-->属性-->“快捷方式”标签 1&#xff0c;修改“起止…

NextGEN Gallery ~ 最强WordPress相册插件

博客照片很多&#xff1f;上传和管理图片太烦&#xff1f;想幻灯显示相册&#xff1f;在博客中任意插入动态图片效果&#xff1f;…… 你和我一样&#xff0c;需要NextGEN Gallery&#xff0c;最强WordPress相册插件&#xff01; 其实网上可以搜到不少关于这个插件的介绍&#…

【经验】网络加速:pip

一、python pip下载加速 参考博客&#xff1a;让PIP源使用国内镜像&#xff0c;提升下载速度和安装成功率。 pip/anaconda修改镜像源&#xff0c;加快python模块安装速度 1、Linux下 修改 ~/.pip/pip.conf (没有就创建一个文件夹及文件。文件夹要加“.”&#xff0c;表示是隐…

iframe 有那些缺

*iframe 会阻塞主页面的 Onload 事 *iframe 和主页面共享连接池&#xff0c;而浏览器对相同域的连接有限制(6-8前)&#xff0c;所以会影响页面的并行加 使用 iframe 之前需要考虑这两个缺点。如果需要使用 iframe&#xff0c;最通过 javascrit 动态给 iframe 添加 src 属性值&a…

用Python让蔡徐坤在我的命令行里打篮球!|附完整代码

点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」全日程揭晓&#xff0c;请扫码咨询 ↑↑↑来源 | 01二进制&#xff08;ID:gh_d1999add1857&#xff09;编辑 | Jane【导语】作者自称是一个经常逛 B 站的肥宅。最近 B 站上流行的视频素材除了“换脸”&#xff0c;其次就要属…

javascript 操作Word和Excel的实现代码

1.保存html页面到word 复制代码 代码如下:<HTML> <HEAD> <title> </title> </HEAD> <body> <form id"form"> <table id "PrintA" width"100%" border"1" cellspacing"0" cel…

【C++】C++11新增关键字详解

目录一、auto1、auto 用来声明自动变量&#xff0c;表明变量存储在栈&#xff08;C11之前&#xff09;2、auto用于推断变量类型示例&#xff08;C11&#xff09;3、声明或定义函数时作为函数返回值的占位符&#xff0c;此时需要与关键字 decltype 一起使用。&#xff08;C11&am…

linux批量创建用户和密码

老男孩教育第五关实战考试题&#xff1a;批量创建10个用户stu01-stu10&#xff0c;并且设置随机8位密码&#xff0c;要求不能用shell的循环&#xff08;例如&#xff1a;for,while等&#xff09;&#xff0c;只能用linux命令及管道实现。 方法1&#xff1a;[rootoldboy /]# ech…

“重构”黑洞:26岁MIT研究生的新算法 | 人物志

点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」全日程揭晓&#xff0c;请扫码咨询 ↑↑↑整理 | 若名出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;这是一个重要时刻。除了发布跟丈夫的两张合照外&#xff0c;Katie Bouman 在 Facebook 上鲜有内容更新&#…

【Ubuntu】VirtualBox显卡驱动VBoxVGA、VBoxSVGA、VMSVGA +3D对播放视频的影响

一、VBOXVGA、VMSVGA、VBOXSVGA简述 VBOXVGA和VBOXSVGA是vbox自己的&#xff0c;SVGA比VGA先进一点&#xff0c; VBoxSVGA: 使用Linux或者 Windows 7或者更高版本的新vm的默认图形控制器。 与传统的VBoxVGA选项相比&#xff0c;此图形控制器可提高性能和3D支持。 VBoxVGA: 将这…

MFC中利用CFileDialog选择文件并读取文件所遇到的问题和解决方法

在用MFC编写一个上位机时&#xff0c;需要实现选择和读取一个二进制文件&#xff0c;本来以为很简单的但是在实现过程中遇到很多问题&#xff0c;所幸都一一解决&#xff0c;这里做一下记录。 首先在实现文件选择&#xff0c;在界面上设置一个按钮&#xff0c;并在点击事件函数…

百度智能云一口气发布 14 个新产品,三大视频解决方案,产品最高降价 50%

产业智能化的浪潮正在加速传统互联网行业的升级&#xff0c;视频行业将成为最大的受益者。4 月 11 日&#xff0c;在 2019ABC INSPIRE 百度云智峰会上&#xff0c;百度副总裁、百度智能云总经理尹世明宣布&#xff0c;“百度云” 品牌全面升级为 “百度智能云”&#xff0c;以 …

开源代码hosting openfoundryfrom tw

http://www.openfoundry.org

倒计时1天!「2019 Python开发者日」报名即将关闭(附参会提醒)

「2019 Python开发者日」倒计时最后1天&#xff0c;仅剩少量余票&#xff0c;请扫码咨询 ↑↑↑相信很多人听过之前的 Python 进入小学课本、Python 进入浙江省高考等新闻&#xff0c;那么&#xff0c;有这么多头衔加持的 Python 究竟魅力在哪&#xff1f;与人工智能、大数据捆…

【Gstreamer】在虚拟机中无法使用硬件加速:gstreamer1.0-vaapi

1、问题描述 在虚拟机中,使用gstreamer播放视频,在没有安装gstreamer1.0-vaapi库时,还是正常的;在安装gstreamer1.0-vaapi后,不能播放视频。 错误信息如下: libva info: VA-API version 0.39.0 libva info: va_getDriverName() returns -1 libva error: va_getDriverNa…

如何在阿里云上安全的存放您的配置 - 续

在《如何在阿里云上安全的存放您的配置》一文中&#xff0c;我们介绍了如何通过ACM存放您的敏感配置&#xff0c;并进行加密。这样做的目的有两个&#xff1a; 在应用程序或对应生产环境容器或系统中&#xff0c;无需持久化任何敏感数据信息(如数据库连接串&#xff0c;等)&…

VLAN-VTP-Trunk

VLAN(Virtual LAN) VLAN可以隔离2层的广播域。A VLAN &#xff1d;&#xff08;一个&#xff09; 广播域 &#xff1d; &#xff08;一个&#xff09;逻辑子网路由器是隔离广播域的单个端口只能承载单个VLAN的流量。使用VLAN好处&#xff1a;1.有效的带宽利用2.提高了安全性3…

科大讯飞刷新纪录,机器阅读理解如何超越人类平均水平? | 技术头条

点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」明日开启&#xff0c;扫码咨询 ↑↑↑记者 | 琥珀出品 | AI科技大本营&#xff08;公众号ID&#xff1a;rgznai100&#xff09;对于日常从事模型训练的研究人员来讲&#xff0c;无论是图像处理还是语音识别&#xff0c;都离…

【经验】Lenovo/ThinkPad 进入BIOS的方法汇总

1、快捷汇总 联想电脑进入BIOS的快捷键有“F2、F1、Del/Delete、NOVO开机”&#xff0c;部分机型按F2、F1时需要FN键配合 2、常用键 Lenovo笔记本&#xff1a;F2 Fn Lenovo台式机&#xff1a;F2 ThinkPad&#xff1a;F1 联系官网说明&#xff1a; http://tsonline.lenovo.…

NO.7 今天我们是实用派,看看业务选择和部署以及常用故障解决方案是怎么做的...

Hello&#xff0c;大家好&#xff0c;这是第七期 上云用户必看期刊&#xff0c;本期我们主打实用派的相关业务选择和部署以及一些常用故障解决方案做分享。 今天我们不平凡&#xff0c;成为实用派 运维工程师需要掌握的技能https://yq.aliyun.com/articles/591171?spma2c4e.11…

php基础知识

一&#xff1a;php变量#一、PHP的变量定义&#xff1a;变量用于存储值&#xff0c;比如数字、文本字符串或数组、五中&#xff1a;string /integer /double /array /object 命名规则:1、PHP的变量名是区分大小写的。 2、变量名必须以$开头 3、变量名开头可以是下划线 4、变量名…

【Qt】QPixmap加载图片报错:Corrupt JPEG data: premature end of data segment Didn‘t expect more than one scan

1、问题描述 在使用QPixmap加载图片时失败&#xff0c;错误信息如下 Corrupt JPEG data: premature end of data segment Didnt expect more than one scan使用QPicture加载时错误信息如下&#xff1a; QPicturePaintEngine::checkFormat: Incorrect header QPicturePaintEng…