go读取excel游戏配置
1.背景
游戏服务器,配置数据一般采用csv/excel来作为载体,这种方式,策划同学配置方便,服务器解析也方便。在jforgame框架里,我们使用以下的excel配置格式。
然后可以非常方便的进行数据检索,例如:
本文使用go实现类似的功能。
2.读取excel
2.1.使用github.com/tealeg/xlsx
库
github.com/tealeg/xlsx
是一个流行的 Go 语言库,用于读取和写入 Excel 文件。
定义数据读取接口,既可以选择excel格式,也可以拓展成csv等格式。
package dataimport "io"type DataReader interface {Read(io.Reader, interface{}) ([]interface{}, error)
}
Excel实现
package dataimport ("encoding/json""fmt""reflect""strconv""strings""github.com/tealeg/xlsx"
)type ExcelDataReader struct {ignoreUnknownFields bool
}func NewExcelDataReader(ignoreUnknownFields bool) *ExcelDataReader {return &ExcelDataReader{ignoreUnknownFields: ignoreUnknownFields,}
}func (r *ExcelDataReader) Read(filePath string, clazz interface{}) ([]interface{}, error) {// 使用 xlsx.OpenFile 打开 Excel 文件xlFile, err := xlsx.OpenFile(filePath)if err != nil {return nil, fmt.Errorf("failed to open Excel file: %v", err)}sheet := xlFile.Sheets[0]rows := sheet.Rowsvar headers []CellHeadervar records [][]CellColumn// 遍历每一行for _, row := range rows {firstCell := getCellValue(row.Cells[0])if firstCell == "HEADER" {headers, err = r.readHeader(clazz, row.Cells)if err != nil {return nil, err}continue}if len(headers) == 0 {continue}record := r.readExcelRow(headers, row)records = append(records, record)if firstCell == "end" {break}}return r.readRecords(clazz, records)
}func (r *ExcelDataReader) readRecords(clazz interface{}, rows [][]CellColumn) ([]interface{}, error) {var records []interface{}clazzType := reflect.TypeOf(clazz).Elem()for _, row := range rows {obj := reflect.New(clazzType).Elem()for _, column := range row {colName := column.Header.Columnif colName == "" {continue}// 根据 Tag 查找字段field, err := findFieldByTag(obj, colName)if err != nil {if !r.ignoreUnknownFields {return nil, err}continue}fieldVal, err := convertValue(column.Value, field.Type())if err != nil {return nil, err}field.Set(reflect.ValueOf(fieldVal))}records = append(records, obj.Interface())}return records, nil
}func (r *ExcelDataReader) readHeader(clazz interface{}, cells []*xlsx.Cell) ([]CellHeader, error) {var headers []CellHeaderfor _, cell := range cells {cellValue := getCellValue(cell)if cellValue == "HEADER" {continue}header := CellHeader{Column: cellValue,}headers = append(headers, header)}return headers, nil
}func getCellValue(cell *xlsx.Cell) string {if cell == nil {return ""}return cell.String()
}func (r *ExcelDataReader) readExcelRow(headers []CellHeader, row *xlsx.Row) []CellColumn {var columns []CellColumnfor i, cell := range row.Cells {// 忽略 header 所在的第一列if i == 0 {continue}if i >= len(headers) {break}cellValue := getCellValue(cell)column := CellColumn{// headers 从 0 开始,所以这里 -1Header: headers[i-1],Value: cellValue,}columns = append(columns, column)}return columns
}func convertValue(value string, fieldType reflect.Type) (interface{}, error) {switch fieldType.Kind() {case reflect.String:return value, nilcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:return strconv.ParseInt(value, 10, 64)case reflect.Float32, reflect.Float64:return strconv.ParseFloat(value, 64)case reflect.Bool:return strconv.ParseBool(value)case reflect.Slice, reflect.Struct:// 处理嵌套的 JSON 对象fieldVal := reflect.New(fieldType).Interface()if err := json.Unmarshal([]byte(value), &fieldVal); err != nil {return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)}return reflect.ValueOf(fieldVal).Elem().Interface(), nildefault:return nil, fmt.Errorf("unsupported type: %v", fieldType.Kind())}
}// 根据 Tag 查找字段
func findFieldByTag(obj reflect.Value, tagValue string) (reflect.Value, error) {objType := obj.Type()for i := 0; i < objType.NumField(); i++ {field := objType.Field(i)tag := field.Tag.Get("excel") // 获取 Tag 值if strings.EqualFold(tag, tagValue) { // 忽略大小写匹配return obj.Field(i), nil}}return reflect.Value{}, fmt.Errorf("field with tag %s not found", tagValue)
}type CellHeader struct {Column stringField reflect.Value
}type CellColumn struct {Header CellHeaderValue string
}
2.2.主要技术点
这里有几个需要注意的点
2.2.1.go结构体变量与excel字段分离
go使用首字母大写来标识一个变量是否包外可见,如果直接使用go的反射api,需要将excel的字段定义成大写,两者强绑定在一起,不方便。为了支持代码与配置命名的分离,可以使用go的tag定义,通过把excel的字段名称,写在struct的tag注释。有点类似于java的注解。
type Item struct {Id int64 `json:"id" excel:"id"`Name string `json:"name" excel:"name"`Quality int64 `json:"quality" excel:"quality"`Tips string `json:"tips" excel:"tips"`Icon string `json:"icon" excel:"icon"`
}
代码片段
// 根据 Tag 查找字段
func findFieldByTag(obj reflect.Value, tagValue string) (reflect.Value, error) {objType := obj.Type()for i := 0; i < objType.NumField(); i++ {field := objType.Field(i)tag := field.Tag.Get("excel") // 获取 Tag 值if strings.EqualFold(tag, tagValue) { // 忽略大小写匹配return obj.Field(i), nil}}return reflect.Value{}, fmt.Errorf("field with tag %s not found", tagValue)
}
2.2.2.exce支持嵌套结构
程序员很喜欢配置直接使用json格式,这样代码具有很高的拓展性,当策划改配置,只要不添加新类型,都可以无需程序介入。(其实大部分策划很讨厌json格式,配置容易出错,而且excel的自动公式无法很智能地工作)
例如下面的配置
结构体定义
type RewardDef struct {Type string `json:"type" excel:"type"`Value string `json:"value" excel:"value"`
}type ConsumeDef struct {Type string `json:"type" excel:"type"`Value string `json:"value" excel:"value"`
}type Mall struct {Id int64 `json:"id" excel:"id"`Type int64 `json:"type" excel:"type"`Name string `json:"name" excel:"name"`Rewards []RewardDef `json:"rewards" excel:"rewards"`Consumes []ConsumeDef `json:"consumes" excel:"consumes"`
}
主要代码
func convertValue(value string, fieldType reflect.Type) (interface{}, error) {switch fieldType.Kind() {case reflect.String:return value, nilcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:return strconv.ParseInt(value, 10, 64)case reflect.Float32, reflect.Float64:return strconv.ParseFloat(value, 64)case reflect.Bool:return strconv.ParseBool(value)case reflect.Slice, reflect.Struct:// 处理嵌套的 JSON 对象fieldVal := reflect.New(fieldType).Interface()if err := json.Unmarshal([]byte(value), &fieldVal); err != nil {return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)}return reflect.ValueOf(fieldVal).Elem().Interface(), nildefault:return nil, fmt.Errorf("unsupported type: %v", fieldType.Kind())}
}
2.3.单元测试用例
由于go只有main包能使用main函数,为了对我们的工具进行测试,我们可以直接使用类的单元测试。
新建一个文件excel_test.go(必须以_test结尾)
package dataimport ("fmt""io/github/gforgame/logger""testing"
)func TestExcelReader(t *testing.T) {// 创建 ExcelDataReader 实例reader := NewExcelDataReader(true)type RewardDef struct {Type string `json:"type" excel:"type"`Value string `json:"value" excel:"value"`}type ConsumeDef struct {Type string `json:"type" excel:"type"`Value string `json:"value" excel:"value"`}type Name struct {Id int64 `json:"id" excel:"id"`Name string `json:"type" excel:"name"`Rewards []RewardDef `json:"rewards" excel:"rewards"`Consumes []ConsumeDef `json:"consumes" excel:"consumes"`}// 读取 Excel 文件result, err := reader.Read("mall.xlsx", &Name{})if err != nil {logger.Error(fmt.Errorf("session.Send: %v", err))}// 打印结果for _, item := range result {fmt.Printf("%+v\n", item)}
}
3.数据载体
3.1.数据容器定义
读取excel文件,得到的是一个记录数组,我们还需要进一步进行封装,方便业务代码使用。
所以我们还需要把这批数据塞入到一个容器里,并且容器应该提供至少以下API。
// GetRecord 根据 ID 获取单个记录
func (c *Container[K, V]) GetRecord(id K) (V, bool) {}// GetAllRecords 获取所有记录
func (c *Container[K, V]) GetAllRecords() []V {}// GetRecordsBy 根据索引名称和索引值获取记录
func (c *Container[K, V]) GetRecordsBy(name string, index interface{}) []V {}
该容器必须支持泛型,适配不同的表定义。代码如下:
package dataimport "fmt"// Container 是一个通用的数据容器,支持按 ID 查询、按索引查询和查询所有记录
type Container[K comparable, V any] struct {data map[K]V // 存储 ID 到记录的映射indexMapper map[string][]V // 存储索引到记录的映射
}// NewContainer 创建一个新的 Container 实例
func NewContainer[K comparable, V any]() *Container[K, V] {return &Container[K, V]{data: make(map[K]V),indexMapper: make(map[string][]V),}
}// Inject 将数据注入容器,并构建索引
func (c *Container[K, V]) Inject(records []V, getIdFunc func(V) K, indexFuncs map[string]func(V) interface{}) {for _, record := range records {id := getIdFunc(record)c.data[id] = record// 构建索引for name, indexFunc := range indexFuncs {indexValue := indexFunc(record)key := indexKey(name, indexValue)c.indexMapper[key] = append(c.indexMapper[key], record)}}
}// GetRecord 根据 ID 获取单个记录
func (c *Container[K, V]) GetRecord(id K) (V, bool) {record, exists := c.data[id]return record, exists
}// GetAllRecords 获取所有记录
func (c *Container[K, V]) GetAllRecords() []V {records := make([]V, 0, len(c.data))for _, record := range c.data {records = append(records, record)}return records
}// GetRecordsBy 根据索引名称和索引值获取记录
func (c *Container[K, V]) GetRecordsBy(name string, index interface{}) []V {key := indexKey(name, index)return c.indexMapper[key]
}// indexKey 生成索引键
func indexKey(name string, index interface{}) string {return fmt.Sprintf("%s@%v", name, index)
}
对于java版本的游戏服务器框架,配置表定义格式如下:
/*** 成就表*/
@Setter
@Getter
@DataTable(name = "achievement")
public class AchievementData {@Idprivate int id;/*** 名字*/private String name;/*** 排序*/private int rank;/*** 类型*/@Indexprivate int type;/*** 条件,每个类型自行定义配置结构*/private String target;}
通过@Id注解定义主键,通过@Index注解定义索引。程序业务代码示例:
// 查询单条记录
AchievementData achievementData = GameContext.dataManager.queryById(AchievementData.class, 1);
// 查询指定索引的所有记录
List<AchievementData> records = GameContext.dataManager.queryByIndex(AchievementData.class, "type", type);
由于go目前不支持注解,无法通过注解让程序自动识别哪一个字段为主键,所以对于每一个容器,需要定义一个函数,手动标识应该取哪一个字段。
// 定义 ID 获取函数和索引函数getIdFunc := func(record Mall) int64 {return record.Id}
按索引取记录的逻辑也是同样的道理。
// 将记录注入容器nameRecords := make([]Mall, len(records))for i, record := range records {nameRecords[i] = record.(Mall)}
单元测试代码
func TestDataContainer(t *testing.T) {// 创建 ExcelDataReaderreader := NewExcelDataReader(true)// 读取 Excel 文件records, err := reader.Read("mall.xlsx", &Mall{})if err != nil {fmt.Println("Failed to read Excel file:", err)return}// 创建 Containercontainer := NewContainer[int64, Mall]()// 定义 ID 获取函数和索引函数getIdFunc := func(record Mall) int64 {return record.Id}indexFuncs := map[string]func(Mall) interface{}{"type": func(record Mall) interface{} {return record.Type},}// 将记录注入容器nameRecords := make([]Mall, len(records))for i, record := range records {nameRecords[i] = record.(Mall)}container.Inject(nameRecords, getIdFunc, indexFuncs)// 查询记录fmt.Println("All records:", container.GetAllRecords())target, _ := container.GetRecord(1)fmt.Println("Record with ID 1:", target)fmt.Println("Records with type 2:", container.GetRecordsBy("type", 2))
}
3.2.适配不同的表配置
从上面的代码可以看出,对于一份excel配置,每次都要复制一段非常相似的代码,无疑非常繁琐。所以我们对以上的代码进一步封装。
首先,定义各种表的元信息(java可通过注解定义)
type TableMeta struct {TableName string // 表名IDField string // ID 字段名IndexFuncs map[string]string // 索引字段名 -> 索引名称RecordType reflect.Type // 记录类型
}
将excel配置注入容器
func ProcessTable(reader *ExcelDataReader, filePath string, config TableMeta) (*Container[int64, interface{}], error) {// 读取 Excel 文件records, err := reader.Read(filePath, reflect.New(config.RecordType).Interface())if err != nil {return nil, fmt.Errorf("failed to read table %s: %v", config.TableName, err)}// 创建 Containercontainer := NewContainer[int64, interface{}]()// 定义 ID 获取函数getIdFunc := func(record interface{}) int64 {val := reflect.ValueOf(record)// 如果 record 是指针,则调用 Elem() 获取实际值if val.Kind() == reflect.Ptr {val = val.Elem()}field := val.FieldByName(config.IDField)return field.Int()}// 定义索引函数indexFuncs := make(map[string]func(interface{}) interface{})if config.IndexFuncs != nil {for indexName, fieldName := range config.IndexFuncs {indexFuncs[indexName] = func(record interface{}) interface{} {val := reflect.ValueOf(record)// 如果 record 是指针,则调用 Elem() 获取实际值if val.Kind() == reflect.Ptr {val = val.Elem()}field := val.FieldByName(fieldName)return field.Interface()}}}// 将记录注入容器container.Inject(records, getIdFunc, indexFuncs)return container, nil
}
在jforgame的版本实现,利用java的类扫描,可以非常方便把所有配置容器一次性扫描并注册,如下:
public void init() {if (!StringUtils.isEmpty(properties.getContainerScanPath())) {Set<Class<?>> containers = ClassScanner.listAllSubclasses(properties.getContainerScanPath(), Container.class);containers.forEach(c -> {// container命名必须以配置文件名+Container,例如配置表为common.csv,则对应的Container命名为CommonContainerString name = c.getSimpleName().replace("Container", "").toLowerCase();containerDefinitions.put(name, (Class<? extends Container>) c);});}Set<Class<?>> classSet = ClassScanner.listClassesWithAnnotation(properties.getTableScanPath(), DataTable.class);classSet.forEach(this::registerContainer);}
go目前不支持类扫描这种元编程,我们只能通过手动注册。
// 定义表配置tableConfigs := []TableMeta{// 商城表{TableName: "mall",IDField: "Id",IndexFuncs: map[string]string{"type": "Type"},RecordType: reflect.TypeOf(Mall{}),},// 道具表{TableName: "item",IDField: "Id",RecordType: reflect.TypeOf(Item{}),},}
3.3.单元测试用例
func TestMultiDataContainer(t *testing.T) {// 创建 ExcelDataReaderreader := NewExcelDataReader(true)// 定义表配置tableConfigs := []TableMeta{// 商城表{TableName: "mall",IDField: "Id",IndexFuncs: map[string]string{"type": "Type"},RecordType: reflect.TypeOf(Mall{}),},// 道具表{TableName: "item",IDField: "Id",RecordType: reflect.TypeOf(Item{}),},}// 处理每张表containers := make(map[string]*Container[int64, interface{}])for _, config := range tableConfigs {container, err := ProcessTable(reader, config.TableName+".xlsx", config)if err != nil {fmt.Printf("Failed to process table %s: %v\n", config.TableName, err)continue}containers[config.TableName] = container}// 查询商城记录mallContainer := containers["mall"]fmt.Println("All records in Mall table:", mallContainer.GetAllRecords())target, _ := mallContainer.GetRecord(1)fmt.Println("Record with ID 1:", target)fmt.Println("Records with type 2 in Mall table:", mallContainer.GetRecordsBy("type", 2))// 查询商城记录itemContainer := containers["item"]fmt.Println("All records in Mall table:", itemContainer.GetAllRecords())target2, _ := itemContainer.GetRecord(1)fmt.Println("Record with ID 1:", target2)
}
完整代码请移步:
--> go游戏服务器
相关文章:
go读取excel游戏配置
1.背景 游戏服务器,配置数据一般采用csv/excel来作为载体,这种方式,策划同学配置方便,服务器解析也方便。在jforgame框架里,我们使用以下的excel配置格式。 然后可以非常方便的进行数据检索,例如ÿ…...
Linux系统下速通stm32的clion开发环境配置
陆陆续续搞这个已经很久了。 因为自己新电脑是linux系统无法使用keil,一开始想使用vscode里的eide但感觉不太好用;后面想直接使用cudeide但又不想妥协,想趁着这个机会把linux上的其他单片机开发配置也搞明白;而且非常想搞懂cmake…...
快慢指针及原理证明(swift实现)
目录 链表快慢指针一、快慢指针基本介绍二、快慢指针之找特殊节点1.删除链表的倒数第k个结点题目描述解题思路 2.链表的中间节点题目描述解题思路 三、快慢指针之环形问题1.判断环形链表题目描述解题思路 2.判断环形链表并返回入环节点题目描述解题思路 3.变种——判断快乐数题…...
web前端3--css
注意(本文一切代码一律是在vscode中书写) 1、书写位置 1、行内样式 //<标签名 style"样式声明"> <p style"color: red;">666</p> 2、内嵌样式 1、style标签 里面写css代码 css与html之间分离 2、css属性:值…...
一文大白话讲清楚webpack基本使用——5——babel的配置和使用
文章目录 一文大白话讲清楚webpack基本使用——5——babel的配置和使用1. 建议按文章顺序从头看,一看到底,豁然开朗2. babel-loader的配置和使用2.1 针对ES6的babel-loader2.2 针对typescript的babel-loader2.3 babel配置文件 一文大白话讲清楚webpack基…...
Python自动化运维:一键掌控服务器的高效之道
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在互联网和云计算高速发展的今天,服务器数量的指数增长使得手动运维和管理变得异常繁琐。Python凭借其强大的可读性和丰富的生态系统,成为…...
基于quartz,刷新定时器的cron表达式
文章目录 前言基于quartz,刷新定时器的cron表达式1. 先看一下测试效果2. 实现代码 前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。 而且听说点赞的人每天的运气都不会太差&…...
HTML常用属性
HTML标签的常见属性包括许多不同的功能,可以为元素提供附加信息或控制元素的行为。以下是一些常见的属性及其解释: 1. src 描述:src(source)属性指定一个资源的路径,通常用于图像、音频、视频等标签。常见…...
在 Babylon.js 中使用 Gizmo:交互式 3D 操作工具
在 3D 应用程序中,交互式操作对象(如平移、旋转、缩放)是一个常见的需求。Babylon.js 提供了一个强大的工具——Gizmo,用于在 3D 场景中实现这些功能。本文将介绍如何在 Babylon.js 中使用 Gizmo,并展示如何通过代码实…...
蓝桥杯练习日常|递归-进制转换
蓝桥云课760数的计算 一、递归 题目: 我的解题代码: #include <iostream> using namespace std; int sum0; int main() {// 请在此输入您的代码int n;cin>>n;int fun(int n);fun(n); cout<<sum<<\n;return 0; } // void fu…...
LabVIEW滤波器选择与参数设置
在信号处理应用中,滤波器是去除噪声、提取目标信号的重要工具。LabVIEW 提供多种类型的滤波器(如低通、高通、带通、带阻),用户需要根据采样频率、信号特性和应用需求合理选择滤波器类型及参数设置。本文以 采样率 100kHz…...
【c语言日寄】Vs调试——新手向
【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是…...
C#中的Timers.Timer使用用法及常见报错
System.Timers.Timer 是一个基于服务器的计时器,它可以在应用程序中定期触发事件。这个计时器特别适合用于多线程环境,并且不应该与用户界面(UI)直接交互。在 ASP.NET 中,通常使用 System.Timers.Timer 来处理周期性的任务。 主要使用步骤&am…...
chrome小插件:长图片等分切割
前置条件: 安装有chrome谷歌浏览器的电脑 使用步骤: 1.打开chrome扩展插件 2.点击管理扩展程序 3.加载已解压的扩展程序 4.选择对应文件夹 5.成功后会出现一个扩展小程序 6.点击对应小程序 7.选择图片进行切割,切割完成后会自动保存 代码…...
mysql数据被误删的恢复方案
文章目录 一、使用备份恢复二、使用二进制日志(Binary Log)三、使用InnoDB表空间恢复四、使用第三方工具预防措施 数据误删是一个严重的数据库管理问题,但通过合理的备份策略和使用适当的恢复工具,可以有效地减少数据丢失的风险…...
K8S-Pod资源清单的编写,资源的增删改查,镜像的下载策略
1. Pod资源清单的编写 1.1 Pod运行单个容器的资源清单 ##创建工作目录 mkdir -p /root/manifests/pods && cd /root/manifests/pods vim 01-nginx.yaml ##指定api版本 apiVersion: v1 ##指定资源类型 kind: Pod ##指定元数据 metadata:##指定名称name: myweb ##用户…...
Unity Line Renderer Component入门
Overview Line Renderer 组件是 Unity 中用于绘制连续线段的工具。它通过在三维空间中的两个或两个以上的点的数组,并在每个点之间绘制一条直线。可以绘制从简单的直线到复杂的螺旋线等各种图形。 1. 连续性和独立线条 连续性:Line Renderer 绘制的线条…...
计算机工程:解锁未来科技之门!
计算机工程与应用是一个充满无限可能性的领域。随着科技的迅猛发展,计算机技术已经深深渗透到我们生活的方方面面,从医疗、金融到教育,无一不在彰显着计算机工程的巨大魅力和潜力。 在医疗行业,计算机技术的应用尤为突出。比如&a…...
翻译:How do I reset my FPGA?
文章目录 背景翻译:How do I reset my FPGA?1、Understanding the flip-flop reset behavior2、Reset methodology3、Use appropriate resets to maximize utilization4、Many options5、About the author 背景 在写博客《复位信号的同步与释放(同步复…...
在Unity中使用大模型进行离线语音识别
文章目录 1、Vosk下载下载vosk-untiy-asr下载模型在项目中使用语音转文字音频转文字2、whisper下载下载unity项目下载模型在unity中使用1、Vosk 下载 下载vosk-untiy-asr Github链接:https://github.com/alphacep/vosk-unity-asr 进不去Github的可以用网盘 夸克网盘链接:h…...
SpringBoot+Vue使用Echarts
前言 在vue项目中使用echarts,本次演示是使用vue2 1 前端准备 echarts官网: https://echarts.apache.org/zh/index.html 官网提供了基本的使用说明和大量的图表 1.1 下载echarts 执行命令 npm install echarts 直接这样执行很可能会失败,…...
【QT】-explicit关键字
explicit explicit 是一个 C 关键字,用于修饰构造函数。它的作用是防止构造函数进行隐式转换。 为什么需要 explicit? 在没有 explicit 的情况下,构造函数可以用于隐式类型转换。这意味着,如果你有一个接受某种类型的参数的构造…...
docker: Device or resource busy
(base) [rootbddx-vr-gpu-bcc2 /]#rm -rf /ssd1/docker/overlay2/8d96a51e3fb78e434fcf2b085e952adcc82bfe37485d427e1e017361a277326d/ rm: cannot remove ‘/ssd1/docker/overlay2/8d96a51e3fb78e434fcf2b085e952adcc82bfe37485d427e1e017361a277326d/merged’: Device or re…...
Vue - toRefs() 和 toRef() 的使用
一、toRefs() 在 Vue 3 中,toRefs()可以将响应式对象的属性转换为可响应的 refs。主要用于在解构响应式对象时,保持属性的响应性。 1. 导入 toRefs 函数 import { toRefs } from vue;2. 将响应式对象的属性转换为 ref const state reactive({count: 0,message:…...
(2024,MLLM,Healthcare,综述)多模态学习是否已在医疗保健领域实现通用智能?
Has Multimodal Learning Delivered Universal Intelligence in Healthcare? A Comprehensive Survey 目录 0. 摘要 1. 简介 5. MLLM 5.1 模态编码器与跨模态适配器 5.1.1 图像编码器 (Image Encoder) 5.1.2 语言模型 (Language Model) 5.1.3 跨模态适配器 (Cross-moda…...
css命名规范——BEM
目录 引言 BEM是什么? 块Block 元素Element 修饰语Modifier BEM解决了哪些问题? 在流行框架的组件中使用 BEM 格式 实战 认识设计图 如何使用当前的css规范正确命名? 引言 css样式类命名难、太难了,难于上青天,这个和js变量命名还不一样。看看项目中五花八门的样…...
使用PHP函数 “is_object“ 检查变量是否为对象类型
在PHP中,变量可以保存不同类型的值,包括整数、字符串、数组、布尔值等等。其中,对象是一种特殊的数据类型,用于封装数据和方法。在处理PHP代码中,我们经常需要检查一个变量是否为对象类型,以便进行相应的处…...
Golang:使用DuckDB查询Parquet文件数据
本文介绍DuckDB查询Parquet文件的典型应用场景,掌握DuckDB会让你的产品分析能力更强,相反系统运营成本相对较低。为了示例完整,我也提供了如何使用Python导出MongoDB数据。 Apache Parquet文件格式在存储和传输大型数据集方面变得非常流行。最…...
Moretl FileSync增量文件采集工具
永久免费: <下载> <使用说明> 我们希望Moretl FileSync是一款通用性很好的文件日志采集工具,解决工厂环境下,通过共享目录采集文件,SMB协议存在的安全性,兼容性的问题. 同时,我们发现工厂设备日志一般为增量,为方便MES,QMS等后端系统直接使用数据,我们推出了增量采…...
消息队列篇--原理篇--Pulsar(Namespace,BookKeeper,类似Kafka甚至更好的消息队列)
Apache Pulusar是一个分布式、多租户、高性能的发布/订阅(Pub/Sub)消息系统,最初由Yahoo开发并开源。它结合了Kafka和传统消息队列的优点,提供高吞吐量、低延迟、强一致性和可扩展的消息传递能力,适用于大规模分布式系…...
linux 扩容
tmpfs tmpfs 82M 0 82M 0% /run/user/1002 tmpfs tmpfs 82M 0 82M 0% /run/user/0 [输入命令]# fdisk -lu Disk /dev/vda: 40 GiB, 42949672960 bytes, 83886080 sectors Units: sectors of 1 * 512 512 bytes Sector size (logi…...
数据表中的数据查询
文章目录 一、概述二、简单查询1.列出表中所有字段2.“*”符号表示所有字段3.查询指定字段数据4.DISTINCT查询 三、IN查询四、BETWEEN ADN查询1.符合范围的数据记录查询2.不符合范围的数据记录查询 五、LIKE模糊查询六、对查询结果排序七、简单分组查询1.统计数量2.统计计算平均…...
深入了解 Java split() 方法:分割字符串的利器
Java 提供的 split() 方法是 String 类中一个常用的工具,它可以将一个字符串根据指定的分隔符切割成多个子字符串,并以字符串数组的形式返回。这个方法常用于字符串的处理、数据解析等场景。本文将详细介绍 Java 中 split() 方法的使用方式,并…...
Ubuntu 安装 docker 配置环境及其常用命令
Docker 安装与配置指南 本文介绍如何在 Ubuntu 系统上安装 Docker,解决权限问题,配置 Docker Compose,代理端口转发,容器内部代理问题等并进行相关的优化设置。参考官方文档:Docker 官方安装指南 一、安装 Docker 1…...
Android Studio安装配置
一、注意事项 想做安卓app和开发板通信,踩了大坑,Android 开发不是下载了就能直接开发的,对于新手需要注意的如下: 1、Android Studio版本,根据自己的Android Studio版本对应决定了你所兼容的AGP(Android…...
leetcode 面试经典 150 题:有效的括号
链接有效的括号题序号20题型字符串解法栈难度简单熟练度✅✅✅ 题目 给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须…...
C语言 指针_野指针 指针运算
野指针: 概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的) 指针非法访问: int main() {int* p;//p没有初始化,就意味着没有明确的指向//一个局部变量不初始化,放…...
【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发之常见布局
目录 1 -> 自适应布局 1.1 -> 线性布局 1.1.1 -> 线性布局的排列 1.1.2 -> 自适应拉伸 1.1.3 -> 自适应缩放 1.1.4 -> 定位能力 1.1.5 -> 自适应延伸 1.2 -> 层叠布局 1.2.1 -> 对齐方式 1.2.2 -> Z序控制 1.3 -> 弹性布局 1.3.1…...
java基础学习——jdbc基础知识详细介绍
引言 数据的存储 我们在开发 java 程序时,数据都是存储在内存中的,属于临时存储,当程序停止或重启时,内存中的数据就会丢失,我们为了解决数据的长期存储问题,有以下解决方案: 通过 IO流书记&…...
第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
第十五届的题目在规定时间内做出了前5道,还有2道找时间再磨一磨。现在把做的一些思路总结如下: 题1:握手问题 问题描述 小蓝组织了一场算法交流会议,总共有 50人参加了本次会议。在会议上,大家进行了握手交流。按照惯例…...
基于JAVA的微信点餐小程序设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
2K320Hz显示器哪个好?
2K320Hz显示器哪个好?320Hz这种高刷新率的显示器确实很少见,那究竟哪个牌子哪个型号更适合你呢? 1.HKC G27H4Pro - 2K320Hz显示器哪个好 外观设计 - HKC G27H4Pro 2K320Hz显示器 三面微边框超震撼:采用三面微边框设计࿰…...
八股学习 微服务篇
微服务篇 常见面试内容Spring Cloud 常见组件注册中心Ribbon负载均衡策略服务雪崩 常见面试内容 Spring Cloud 常见组件 Spring Cloud有5个常见组件: Eureka/Nacos:注册中心;Ribbon:负载均衡;Feign:远程调用;Hystrix/Sentinel:服…...
C# 中 readonly 与 const 的使用
总目录 前言 在C#编程中,readonly 和 const 是两个用于定义不可变数据的关键字。它们都旨在创建那些一旦赋值后就不能再改变的字段或变量。尽管这两个关键字看起来相似,但它们有着不同的特性和适用场景。本文将深入探讨 readonly 和 const 的区别&#…...
Spring Boot Starter介绍
前言 大概10来年以前,当时springboot刚刚出现并没有流行,当时的Java开发者们开发Web应用主要是使用spring整合springmvc或者struts、iBatis、hibernate等开发框架来进行开发。项目里一般有许多xml文件配置,其中配置了很多项目中需要用到的Be…...
Kafak 单例生产者实现-C#操作
前面写了一篇入门操作的文章,因为工作需要,简单修改了下如何实现单例生产者。 Kafka入门-C#操作_c# kafka-CSDN博客文章浏览阅读1.6k次,点赞20次,收藏9次。2).报错:“kafka.zookeeper.ZooKeeperClientTimeoutException: Timed out waiting for connection while in state…...
软件开发学习路线——roadmap
推荐软件学习路线网站:https://roadmap.sh/get-started 有有关前端后端开发的学习路径,也有AI,移动开发,管理相关的学习路径 会有相应的词条路径,深入学习 右上角可以设置学习任务的完成情况...
移动端VR处理器和传统显卡的不同
骁龙 XR 系列芯片 更多地依赖 AI 技术 来优化渲染过程,而传统的 GPU 渲染 则倾向于在低画质下运行以减少负载。这种设计是为了在有限的硬件资源下(如移动端 XR 设备)实现高性能和低功耗的平衡。以下是具体的分析: 1. AI 驱动的渲染…...
Jenkins下载 Maven、Allure 插件并且配置环境
文章目录 Jenkins在插件中心下载 maven、allure插件maven插件下载allure插件下载 配置maven、allure 往期推荐: 最新! 在 Linux上搭建Jenkins环境! Jenkins邮件通知的详细配置含邮件通知模板! Jenkin配置企业微信通知 Jenkins在插件中心下载 maven、…...
C# OpenCV机器视觉:连通域分割
在一个阳光明媚得有些 “嚣张” 的午后,阿强像只好奇的小松鼠,一头扎进了他那乱得像被打劫过的实验室。这实验室里,各种电路板、奇形怪状的传感器和缠成一团的电线肆意横陈,仿佛在诉说着主人平日里为科研疯狂的 “战斗” 痕迹。阿…...