当前位置: 首页 > article >正文

【Viper】配置格式与支持的数据源与go案例

Viper 是一个用于 Go 应用程序的配置管理库,支持多种配置格式和数据源。


安装依赖

go get github.com/spf13/viper
go get github.com/spf13/viper/remote
go get go.etcd.io/etcd/client/v3

"github.com/spf13/viper/remote"要写在etcd客户端import里

1. 配置格式—就是可以读的配置类型

Viper 支持多种常见的配置格式,包括:

  • JSON: 一种轻量级的数据交换格式,易于阅读和编写。
  • YAML: 一种人类可读的数据序列化格式,常用于配置文件。
  • TOML: 一种易于阅读的配置文件格式,旨在成为最小的配置文件格式。
  • HCL: HashiCorp 配置语言,用于描述基础设施配置。
  • Java Properties: 一种简单的键值对格式,常用于 Java 应用程序。
  • INI: 一种简单的配置文件格式,常用于 Windows 应用程序。
  • Envfile: 环境变量文件格式,通常用于存储环境变量。

2. 支持的数据源—可以从哪里导入要读的配置

Viper 不仅支持从文件中读取配置,还支持从多种数据源获取配置,包括:

  • 文件: 从本地文件系统中读取配置文件。
  • 环境变量: 从操作系统的环境变量中读取配置。
  • io.Reader: 从Reader中读取配置。
  • 远程配置系统: 从远程配置系统(如 etcd、Consul)中读取配置。

Viper 支持的四种常见数据源的简单实现:

1. 文件

Viper 支持从多种文件格式中读取配置,包括 JSON、YAML、TOML、HCL、INI 等。

使用方法

  • 设置配置文件路径和名称
    viper.SetConfigName("config") // 配置文件名称(不带扩展名)
    viper.SetConfigType("yaml")   // 配置文件类型(如 yaml、json 等)
    viper.AddConfigPath(".")      // 配置文件搜索路径
    
  • 读取配置文件
    if err := viper.ReadInConfig(); err != nil {log.Fatalf("Error reading config file: %s", err)
    }
    

示例

假设有一个 config.yaml 文件:

database:host: localhostport: 5432

读取配置:

viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {log.Fatal(err)
}
fmt.Println("Database Host:", viper.GetString("database.host"))
fmt.Println("Database Port:", viper.GetInt("database.port"))

2. 环境变量

Viper 可以从操作系统的环境变量中读取配置,适合在容器化或云原生环境中使用。

使用方法

  • 启用环境变量支持
    viper.AutomaticEnv() // 自动绑定环境变量
    
  • 设置环境变量前缀(可选):
    viper.SetEnvPrefix("MYAPP") // 环境变量前缀(如 MYAPP_DATABASE_HOST)
    
  • 绑定特定键到环境变量
    viper.BindEnv("database.host", "DB_HOST") // 将 database.host 绑定到环境变量 DB_HOST
    

示例

假设设置了环境变量:

export DB_HOST=localhost
export DB_PORT=5432

读取配置:

viper.AutomaticEnv()
viper.BindEnv("database.host", "DB_HOST")
viper.BindEnv("database.port", "DB_PORT")
fmt.Println("Database Host:", viper.GetString("database.host"))
fmt.Println("Database Port:", viper.GetInt("database.port"))

3. io.Reader

Viper 支持从实现了 io.Reader 接口的对象中读取配置,适合从内存或网络流中加载配置。

使用方法

  • io.Reader 读取配置
    configData := []byte(`{"database": {"host": "localhost", "port": 5432}}`)
    reader := bytes.NewReader(configData)
    viper.SetConfigType("json") // 设置配置类型
    if err := viper.ReadConfig(reader); err != nil {log.Fatal(err)
    }
    

示例

configData := []byte(`
database:host: localhostport: 5432
`)
reader := bytes.NewReader(configData)
viper.SetConfigType("yaml")
if err := viper.ReadConfig(reader); err != nil {log.Fatal(err)
}
fmt.Println("Database Host:", viper.GetString("database.host"))
fmt.Println("Database Port:", viper.GetInt("database.port"))

4. etcd

Viper 支持从 etcd(分布式键值存储)中读取配置,适合分布式系统的配置管理。

使用方法

  • 安装 etcd 支持
    go get github.com/spf13/viper/remote
    
  • 配置 etcd 客户端
    import ("github.com/spf13/viper"_ "github.com/spf13/viper/remote"
    )
    
  • 从 etcd 读取配置
    viper.AddRemoteProvider("etcd", "http://127.0.0.1:2379", "/config/path")
    viper.SetConfigType("yaml") // 设置配置类型
    if err := viper.ReadRemoteConfig(); err != nil {log.Fatal(err)
    }
    

示例

假设 etcd 中存储了以下配置:

database:host: localhostport: 5432

读取配置:

viper.AddRemoteProvider("etcd", "http://127.0.0.1:2379", "/config/path")
viper.SetConfigType("yaml")
if err := viper.ReadRemoteConfig(); err != nil {log.Fatal(err)
}
fmt.Println("Database Host:", viper.GetString("database.host"))
fmt.Println("Database Port:", viper.GetInt("database.port"))

总结

  • 文件:适合本地开发和静态配置。
  • 环境变量:适合容器化或云原生环境。
  • io.Reader:适合从内存或网络流中加载配置。
  • etcd:适合分布式系统的动态配置管理。

综合go案例

config.env

env=dev
server.ip=127.0.0.1
server.port=8080
courses=["golang", "C/C++", "音视频", "kernel", "dpdk", "面试题", "游戏"]

config.json

{"env": "dev","server": {"ip":"127.0.0.1","port": 8085},"courses": ["golang","C/C++","音视频","kernel","dpdk","面试题","游戏"],"list": [{"name": "golang","author": "nick"},{"name": "C/C++","author": "king"},{"name": "kernel","author": "vico"},{"name": "音视频","author": "Darren"},{"name": "游戏","author": "mark"}]
}

config.noext

# 键值
env: dev
# 对象或map
server:ip: 127.0.0.1port: 8080
# 数组或列表
courses:- "golang"- "C/C++"- "音视频"- "kernel"- "dpdk"- "面试题"- "游戏"list:- name: golangauthor: nick- name: C/C++author: king- name: kernelauthor: vico- name: 音视频author: Darren- name: 游戏author: mark

config.yaml

# 键值
env: dev
# 对象或map
server:ip: 127.0.0.1port: 8080
# 数组或列表
courses:- "golang"- "C/C++"- "音视频"- "kernel"- "dpdk"- "面试题"- "游戏"list:- name: golangauthor: nick- name: C/C++author: king- name: kernelauthor: vico- name: 音视频author: Darren- name: 游戏author: mark

data_source.go

package configimport ("github.com/spf13/viper"_ "github.com/spf13/viper/remote""io""log"
)// LoadFromFile 加载配置文件。
// 该函数使用 vipers 去从指定的文件路径加载配置。
// 参数:
//
//	filepath - 配置文件的路径。
//	typ - 可选参数,指定配置文件的类型。
//
// 返回值:
//
//	*viper.Viper - 加载了配置文件数据的 viper 实例。
//	error - 如果加载配置文件时发生错误,返回该错误。
func LoadFromFile(filepath string, typ ...string) (*viper.Viper, error) {// 创建一个新的 viper 实例。v := viper.New()// 设置配置文件的路径。v.SetConfigFile(filepath)// 如果提供了配置文件类型,则设置配置文件类型。if len(typ) > 0 {v.SetConfigType(typ[0])}// 读取并解析配置文件到viper中。err := v.ReadInConfig()// 返回 viper 实例和可能的错误。return v, err
}// LoadFromEnv 初始化一个viper实例,并将其配置为自动从环境变量中加载配置。
// 该函数返回一个指向viper实例的指针,以及一个错误(此示例中未实现错误处理)。
func LoadFromEnv() (*viper.Viper, error) {// 创建一个新的viper实例。v := viper.New()// 启用自动环境变量加载,viper将自动查找与结构体字段同名的环境变量。//v.AutomaticEnv()// 手动绑定环境变量GOPATH和GOROOT到viper实例。// 这样做是为了方便地从环境变量中读取这些值,而无需手动查询环境变量。v.BindEnv("GOPATH")v.BindEnv("GOROOT")// 返回viper实例指针,以及nil作为错误值,表示没有发生错误。return v, nil
}// LoadFromIoReader 从 io.Reader 类型的参数中加载配置信息。
// 此函数主要用于从不同的读取源(如文件、网络流等)中加载配置,
// 并根据提供的 typ 参数设置配置的类型。
// 参数:
//   - reader: io.Reader 类型的接口,代表任何可以读取字节流的对象。
//   - typ: 字符串,指定配置文件的类型(例如 "json"、"yaml")。
//
// 返回值:
//   - *viper.Viper: 一个指向 viper.Viper 实例的指针,用于进一步操作或获取配置信息。
//   - error: 在读取配置过程中遇到的错误(如果有)。
func LoadFromIoReader(reader io.Reader, typ string) (*viper.Viper, error) {// 创建一个新的 viper 实例。v := viper.New()// 设置配置类型,根据传入的 typ 参数。v.SetConfigType(typ)// 从 reader 参数指定的源中读取配置信息。err := v.ReadConfig(reader)// 返回 viper 实例和可能的错误。return v, err
}// LoadFromEtcd loadFromEtcd 从 Etcd 中加载配置信息。
// 参数:
//
//	etcdAddr: Etcd服务器的地址。
//	key: 配置在Etcd中的键路径。
//	typ: 配置文件的类型。
//
// 返回值:
//
//	*viper.Viper: 加载配置后的Viper实例指针。
//	error: 错误信息,如果有的话。
func LoadFromEtcd(etcdAddr, key, typ string) (*viper.Viper, error) {// 创建一个新的Viper实例。v := viper.New()// 为Viper实例添加Etcd3远程提供者。// 这里可能会出现错误,如果提供的Etcd地址或键路径无效。err := v.AddRemoteProvider("etcd3", etcdAddr, key)if err != nil {// 记录错误信息。log.Println(err)// 返回nil和错误信息。return nil, err}// 设置Viper实例的配置类型。v.SetConfigType(typ)// 从远程提供者读取配置信息。// 这里可能会出现错误,如果无法从远程提供者获取配置信息。err = v.ReadRemoteConfig()// 返回Viper实例和可能的错误信息。return v, err
}

main.go

package mainimport ("bytes""context""fmt"clientv3 "go.etcd.io/etcd/client/v3""golang20-viper/config""log""os"
)func main() {//loadFile()//loadEnv()//loadReader()loadEtcd()
}// loadFile 函数演示了如何从不同格式的配置文件中加载配置。
// 它依次尝试加载 .env, .json, .yaml, .toml 和 .yaml 文件,并打印出特定配置项的值。
func loadFile() {// 从 "config.env" 文件加载配置,并打印出环境、服务器端口和课程信息。v1, err := config.LoadFromFile("config.env", "env")fmt.Println("config.env", err, v1.Get("env"), v1.Get("server.port"), v1.Get("courses"))// 从 "config.json" 文件加载配置,包括环境、服务器端口、课程信息和作者信息。v2, err := config.LoadFromFile("config.json")fmt.Println("config.json", err, v2.Get("env"), v2.Get("server.port"), v2.Get("courses").([]any)[0], v2.Get("list").([]any)[0].(map[string]any)["author"])// 从 "config.noext" 文件加载 YAML 格式的配置,同样打印环境、服务器端口、课程信息和作者信息。v3, err := config.LoadFromFile("config.noext", "yaml")fmt.Println("config.noext", err, v3.Get("env"), v3.Get("server.port"), v3.Get("courses").([]any)[0], v3.Get("list").([]any)[0].(map[string]any)["author"])// 从 "config.toml" 文件加载配置,展示如何获取嵌套配置项的值。v4, err := config.LoadFromFile("config.toml")fmt.Println("config.toml", err, v4.Get("env"), v4.Get("server.port"), v4.Get("courses").(map[string]any)["list"].([]any)[0], v4.Get("list").([]any)[0].(map[string]any)["author"])// 从 "config.yaml" 文件加载配置,再次打印出环境、服务器端口、课程信息和作者信息,验证不同格式文件的兼容性。v5, err := config.LoadFromFile("config.yaml")fmt.Println("config.yaml", err, v5.Get("env"), v5.Get("server.port"), v5.Get("courses").([]any)[0], v5.Get("list").([]any)[0].(map[string]any)["author"])
}func loadEnv() {v, err := config.LoadFromEnv()fmt.Println(err, v.Get("GOROOT"), v.Get("gopath"))
}// loadReader 读取配置文件并解析特定配置项。
// 该函数没有输入参数和返回值。
// 功能描述:
// 1. 读取名为 "config.yaml" 的配置文件。
// 2. 如果读取过程中遇到错误,记录错误信息并终止程序运行。
// 3. 使用 bytes.NewReader 创建一个字节流读取器来读取配置文件内容。
// 4. 调用 config.LoadFromIoReader 函数从字节流读取器中加载配置信息。
// 5. 打印解析后的配置项,包括环境变量、服务器端口、课程信息和作者信息。
func loadReader() {// 读取配置文件 "config.yaml" 的内容到 byteList。byteList, err := os.ReadFile("config.yaml")if err != nil {// 如果读取配置文件时发生错误,记录错误信息并终止程序。log.Fatalln(err)}// 创建一个新的字节流读取器来读取配置文件内容。r := bytes.NewReader(byteList)// 从字节流读取器中加载配置信息,并处理可能的错误。v, err := config.LoadFromIoReader(r, "yaml")// 打印解析后的配置项。fmt.Println("io.reader", err, v.Get("env"), v.Get("server.port"), v.Get("courses").([]any)[0], v.Get("list").([]any)[0].(map[string]any)["author"])
}// loadEtcd 函数用于从本地文件加载配置并写入到 etcd 中,然后从 etcd 中读取配置并打印部分配置项。
// 该函数不接收任何参数,也不返回任何值。
func loadEtcd() {// 定义 etcd 的地址、配置项的键以及本地配置文件的路径etcdAddr := "192.168.88.131:2379"key := "/0voice/viper/config.yaml"loaclFilepath := "config.yaml"// 将本地配置文件的内容写入到 etcd 中writeConfToEtcd(etcdAddr, key, loaclFilepath)// 从 etcd 中加载配置,并解析为 yaml 格式v, err := config.LoadFromEtcd(etcdAddr, key, "yaml")// 打印从 etcd 中读取的配置项,包括环境、服务器端口、课程列表中的第一个课程以及列表中的第一个作者的名称fmt.Println("etcd", err, v.Get("env"), v.Get("server.port"), v.Get("courses").([]any)[0], v.Get("list").([]any)[0].(map[string]any)["author"])//fmt.Println(err, v)
}// writeConfToEtcd 将本地配置文件内容写入到etcd指定键中
// 参数说明:
//   - etcdAddr: etcd服务地址,格式为"IP:PORT"
//   - key: etcd中存储配置的键名
//   - localFilepath: 本地配置文件的路径
//
// 功能说明:
//
//	读取配置文件,将其内容存入etcd集群的指定键中
//	遇到任何错误(文件读取、连接etcd、写入etcd)将直接终止程序
func writeConfToEtcd(etcdAddr, key, localFilepath string) {// 从固定路径读取配置文件内容byteList, err := os.ReadFile(localFilepath)if err != nil {// 如果读取配置文件时发生错误,记录错误信息并终止程序。log.Fatalln(err)}v := string(byteList)// 创建etcd客户端连接cli, err := clientv3.New(clientv3.Config{Endpoints: []string{etcdAddr},})if err != nil {log.Fatal(err)}// 将配置内容写入etcd指定键_, err = cli.Put(context.Background(), key, v)if err != nil {log.Fatal(err)}
}

etcd docker部署

docker run -d \
-p 2379:2379 \
-p 2380:2380 \
--restart always \
--volume=/home/etcd:/etcd-data \
--name etcd quay.io/coreos/etcd:v3.5.7 \
/usr/local/bin/etcd \
--data-dir=/etcd-data --name node1 \
--initial-advertise-peer-urls http://192.168.239.161:2380 \
--listen-peer-urls http://0.0.0.0:2380 \
--advertise-client-urls http://192.168.239.161:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--initial-cluster node1=http://192.168.239.161:2380 \
--initial-cluster-token tkn \
--initial-cluster-state new

https://github.com/0voice

相关文章:

【Viper】配置格式与支持的数据源与go案例

Viper 是一个用于 Go 应用程序的配置管理库,支持多种配置格式和数据源。 安装依赖 go get github.com/spf13/viper go get github.com/spf13/viper/remote go get go.etcd.io/etcd/client/v3"github.com/spf13/viper/remote"要写在etcd客户端import里 1…...

C++ Primer 参数传递

欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...

数据结构 day06

数据结构 day06 6. 双向链表6.3. 双向循环链表 7. 树 tree7.1. 特点7.1.1. 什么是树7.1.2. 树的特性7.1.3. 关于树的一些术语 7.2. 二叉树7.2.1. 什么是二叉树7.2.2. 二叉树的性质7.2.3. 满二叉树和完全二叉树的区别7.2.4. 二叉树的遍历(画图)7.2.5. 二叉…...

AI编程01-生成前/后端接口对表-豆包(或Deepseek+WPS的AI

前言: 做过全栈的工程师知道,如果一个APP的项目分别是前端/后端两个团队开发的话,那么原型设计之后,通过接口文档进行开发对接是非常必要的。 传统的方法是,大家一起定义一个接口文档,然后,前端和后端的工程师进行为何,现在AI的时代,是不是通过AI能协助呢,显然可以…...

01什么是DevOps

在日常开发中,运维人员主要负责跟生产环境打交道,开发和测试,不去操作生产环境的内容,生产环境由运维人员操作,这里面包含了环境的搭建、系统监控、故障的转移,还有软件的维护等内容。 当一个项目开发完毕&…...

力扣100. 相同的树(利用分解思想解决)

Problem: 100. 相同的树 文章目录 题目描述思路Code 题目描述 思路 题目要求判断两个二叉树是否完全相同,而此要求可以利用问题分解的思想解决,即判断当前节点的左右子树是否完全相同,而在二叉树问题分解的一般题目中均会带有返回值&#xff…...

【深度学习模型分类】

深度学习模型种类繁多,涵盖了从基础到前沿的多种架构。以下是主要模型的分类及代表性方法: 1. 基础模型 1.1 多层感知机(MLP) 特点:全连接神经网络,适用于结构化数据。 应用:分类、回归任务…...

el-select 设置宽度 没效果

想实现下面的效果,一行两个,充满el-col12 然后设置了 width100%,当时一直没有效果 解决原因: el-form 添加了 inline 所以删除inline属性 即可...

chrome://version/

浏览器输入: chrome://version/ Google浏览器版本号以及安装路径 Google Chrome131.0.6778.205 (正式版本) (64 位) (cohort: Stable) 修订版本81b36b9535e3e3b610a52df3da48cd81362ec860-refs/branch-heads/6778_155{#8}操作系统Windows…...

反向代理块sjbe

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求,然后将请求转发给内部网络上的服务器,将从服务器上得到的结果返回给客户端,此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说,反向代理就相当于…...

封装一个sqlite3动态库

作者:小蜗牛向前冲 名言:我可以接受失败,但我不能接受放弃 如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、项目案例 二…...

P1878 舞蹈课(详解)c++

题目链接:P1878 舞蹈课 - 洛谷 | 计算机科学教育新生态 1.题目解析 1:我们可以发现任意两个相邻的都是异性,所以他们的舞蹈技术差值我们都要考虑,4和2的差值是2,2和4的差值是2,4和3的差值是1,根…...

力扣第一题 哈希解法 O(n)时间复杂度

题目: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那俩个整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 你可以按任意顺序返…...

【C++学习篇】C++11

目录 ​编辑 1. 初始化列表{} 1.1 C98中的{} 1.2 C11中的{} 2. C11中的std::initializer_list 3. 右值引用和移动语义 3.1 左值和右值 3.2 左值引用和右值引用 3.3 引用延长生命周期 3.4 左值和右值的参数匹配 3.5 右值引⽤和移动语义的使⽤场景 3.5.1 左值引⽤…...

leetcode刷题第十天——栈与队列Ⅱ

本次刷题顺序是按照卡尔的代码随想录中给出的顺序 1047. 删除字符串中的所有相邻重复项 char* removeDuplicates(char* s) {int len strlen(s);char* tmp malloc(sizeof(char) * (len 1));int top -1, idx 0;while(idx < len) {if(top -1) tmp[top] s[idx];else {i…...

Vulnhub靶机随笔-Hackable II

Vulnhub靶机Hackable II详解 攻击机Kali IP:192.168.1.6 靶机 IP:未知 系统:未知 A.信息收集 扫描靶机存活性 确定IP地址 1.命令:arp-scan -l 扫描靶机开放端口及其服务版本信息 2.命令:nmap -A -p- -sV 靶机IP 3.靶机开放三个端口: 21ftp端口:存在anonymous匿…...

适配器模式 + 外观模式联合使用:新旧系统的平滑整合之道

🌟 引言:当系统演进遇到历史包袱 场景痛点: 假设企业需要将老旧的CRM系统与新的SaaS平台整合,面临: 旧系统接口:XML格式+同步调用新系统接口:JSON格式+异步调用需要统一提供简洁的RESTful API给前端若直接修改旧系统: // 旧系统核心类(无法修改) public class Leg…...

九.Spring Boot使用 ShardingSphere + MyBatis + Druid 进行分库分表

文章目录 前言一、引入依赖二、创建一个light-db_1备用数据库三、配置文件 application-dev.yml四、创建shardingsphere-config.yml完整项目结构 五、测试总结 前言 在现代化微服务架构中&#xff0c;随着数据量的不断增长&#xff0c;单一数据库已难以满足高可用性、扩展性和…...

【第2章:神经网络基础与实现——2.3 多层感知机(MLP)的构建与调优技巧】

在当今科技飞速发展的时代,人工智能早已不是一个陌生的词汇,它已经渗透到我们生活的方方面面,从智能语音助手到自动驾驶汽车,从图像识别到自然语言处理。而支撑这一切的核心技术之一,就是神经网络。作为机器学习领域的璀璨明星,神经网络已经在众多任务中取得了令人瞩目的…...

宠物企业宣传网站静态模板 – 前端静态页面开发实例

该宠物宣传企业站是一个基于前端技术构建的静态网站&#xff0c;旨在为宠物行业的企业提供一个简洁、现代的在线展示平台。整个网站采用HTML、CSS和JavaScript三种技术&#xff0c;确保了良好的用户体验和页面表现。 前端技术&#xff1a; HTML&#xff1a;HTML负责构建网站的…...

git如何下载指定版本

要使用Git下载指定版本&#xff0c;可以通过以下步骤进行操作‌&#xff1a; ‌1. 使用Git命令行下载指定版本‌&#xff1a; 1.1 首先&#xff0c;使用git clone命令克隆整个git库到本地。例如&#xff1a;git clone [库的URL]。这将下载最新的代码到本地。‌ 1.2 进入克隆…...

【第4章:循环神经网络(RNN)与长短时记忆网络(LSTM)——4.2 LSTM的引入与解决长期依赖问题的方法】

在人工智能的璀璨星空中,深度学习模型犹如一颗颗耀眼的星辰,引领着技术的革新。而在处理序列数据的领域中,循环神经网络(RNN)无疑是那颗最为亮眼的星星。然而,即便是这样强大的模型,也面临着一些棘手的问题,其中最突出的便是长期依赖问题。今天,我们就来深入探讨一下长…...

IoTDB 集群节点 IP 改变,如何更新集群

问题 问题1&#xff1a;如果 IoTDB 配置的时候用的 IP&#xff0c;没有用 hostname&#xff0c;后面 IP 修改了&#xff0c;历史数据需要重新导吗&#xff1f; 问题2&#xff1a;如果现场运行 IoTDB 半年&#xff0c;电脑 IP 要改的话&#xff0c;半年的数据要导出来再导入么…...

C++ 设计模式-建造者模式

以下是一个完整的C建造者模式示例&#xff0c;包含产品类、建造者接口、具体建造者、指挥者以及测试代码&#xff1a; #include <iostream> #include <string> #include <memory>// 产品类&#xff1a;汽车 class Car { public:void setBody(const std::str…...

词袋模型和词嵌入模型区别和关联分析(词袋模型是否属于词嵌入模型)

词袋模型&#xff08;Bag of Words, BoW&#xff09;不属于词嵌入模型&#xff0c;它们是两种完全不同的文本表示方法。以下从多个维度对比二者的核心区别 1. 本质区别 特性词袋模型 (BoW)词嵌入模型 (Word Embedding)表示形式离散的稀疏向量&#xff08;高维&#xff0c;维度…...

png、jpg、gif、webp的区别

png、jpg、gif、webp的区别 1.img的格式2.问题 1.img的格式 png 无损压缩,尺寸体积比jpg/jpeg大;适合做小图标jpg 采用了压缩算法,有一点失真,比png体积小;适合中大型图片gif 动态图webp 同时支持有损和无损压缩,相同质量的图片,webp具有更小的体积,但兼容性不太好(在某些浏览…...

el-input输入框样式修改

el-input输入框样式修改 目的&#xff1a;蓝色边框去掉、右下角黑色去掉(可能看不清楚) 之前我试过deep不行 最有效的办法就是就是在底部添加一下css文件 代码中针对input的type为textarea&#xff0c;对于非textarea&#xff0c;只需将下面的css样式中的textarea替换成input…...

什么是多光谱环形光源

多光谱环形光源是一种用于机器视觉、工业检测和科学研究的光源设备&#xff0c;能够提供多种波长的光&#xff0c;适用于不同材料和表面的检测需求。以下是其关键特点和应用&#xff1a; 关键特点 多光谱输出&#xff1a;可发射多种波长的光&#xff08;如可见光、红外光、紫外…...

几款C#开发的入门书籍与视频教程

以下是几本适合C#初学者的书籍和一些优质的视频教程推荐&#xff0c;帮助你快速入门C#开发&#xff1a; 书籍推荐 1. 《C#入门经典》 • 作者&#xff1a;Karli Watson, Christian Nagel 等 • 特点&#xff1a;经典的C#入门书籍&#xff0c;内容全面&#xff0c;从基础语法到…...

日常问题-pnpm install执行没有node_modules生成

日常问题-pnpm install执行没有node_modules生成 1.问题2.解决方法 1.问题 执行pnpm i后&#xff0c;提示Scope: all 3 workspace projects Done in 503ms&#xff0c;而且没有node_modules生成。很奇怪 2.解决方法 确保根目录有 pnpm-workspace.yaml 文件&#xff1a; 把这…...