各类软件的版本号定义虽然都不尽相同,但是其基本原理基本上还是相通的:通过特写的字符对字符串进行分割。我们把这一规则稍作整理,放到 struct tag 中,告诉解析器如何解析,下面就以 semver 为例作个示范:

type SemVersion struct {
Major int `version:"0,.1"`
Minor int `version:"1,.2"`
Patch int `version:"2,+4,-3"`
PreRelease string `version:"3,+4"`
Build string `version:"4"`
}
  1. 其中 struct tag 中的第一段内容表示的是当前字段的一个编号,要求唯一且为数值,0 表示入口;
  2. 后面的是解析规则,可以有多条,以逗号分隔,优先级等同;
  3. 每一条规则的第一个字符为触发条件,之后的数字即为前面的编号,当解析器碰到该字符时,即结束当前字段的解析,跳转到其后面指定的编号字段。

如何实现

首先定义一个表示每个字段的结构:

 type struct field {
value reflect.Value // 指赂字段的值
routes map[byte]int // 解析的跳转规则
}

然后将整个结构体解析到一个 map 中,其键名即为字段的编号:

func getFields(obj interface{}) (map[int]*field, error) {
v := reflect.ValueOf(obj)
t := v.Type()
fields := make(map[int]*field, v.NumField()) for i := 0; i < v.NumField(); i++ {
tags := strings.Split(t.Field(i).Tag.Get("version"), ",")
if len(tags) < 1 {
return nil, errors.New("缺少标签内容")
} index, err := strconv.Atoi(tags[0])
if err != nil {
return nil, err
}
if _, found := fields[index]; found {
return nil, errors.New("重复的字段编号")
} field := &field{routes: make(map[byte]int, 2)} for _, vv := range tags[1:] {
n, err := strconv.Atoi(vv[1:])
if err != nil {
return nil, err
}
field.routes[vv[0]] = n
} field.value = v.Field(i)
fields[index] = field
} return fields, nil
}

然后通过一个函数将字符串解析到结构中:

func Parse(obj interface{}, ver string) {
fields, _ := getFields(obj) start := 0
field := fields[0]
for i := 0; i < len(ver)+1; i++ {
var nextIndex int
if i < len(ver) { // 未结束
index, found := field.routes[ver[i]]
if !found {
continue
}
nextIndex = index
} switch field.value.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
n, err := strconv.ParseInt(ver[start:i], 10, 64)
if err != nil {
panic(err)
}
field.value.SetInt(n)
case reflect.String:
field.value.SetString(ver[start:i])
default:
panic("无效的类型")
} i++ // 过滤掉当前字符
start = i
field = fields[nextIndex] // 下一个 field
} // end for
}

之后我们只需要定义各类版本号的相关结构体,然后传给 Parse 就可以了:

// Major_Version_Number.Minor_Version_Number[.Revision_Number[.Build_Number]]
type GNUVersion struct {
Major int `version:"0,.1"`
Minor int `version:"1,.2"`
Revision int `version:"2, 3"`
Build string `version:"3"`
} gnu := &GNUVersion{}
sem := &SemVersion{}
Parse(gnu, "1.2.0 build-1124")
Parse(sem, "1.2.0+20160615")

查看完整的实现:https://github.com/issue9/version

使用 Go 的 struct tag 来解析版本号字符串的更多相关文章

  1. 【linux】U-BOOT与linux kernel通信: struct tag

      欢迎转载,转载时需保留作者信息. 邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http://b ...

  2. 引用 U-boot给kernel传参数和kernel读取参数—struct tag

    引用 清风徐徐 的 U-boot给kernel传参数和kernel读取参数—struct tag U-boot会给Linux Kernel传递很多参数,如:串口,RAM,videofb等.而Linux ...

  3. Go语言中的struct tag

    有时在Go的结构体定义时会看到这样的形式: type User struct { UserId int `json:"user_id" bson:"b_user_id&q ...

  4. java后台处理解析json字符串的两种方式

    简单说一下背景 上次后端通过模拟http请求百度地图接口,得到的是一个json字符串,而我只需要其中的某个key对应的value. 当时我是通过截取字符串取的,后来觉得不太合理,今天整理出了两种处理解 ...

  5. js中解析json对象:JSON.parse()用于从一个字符串中解析出json对象, JSON.stringify()用于从一个对象解析出字符串。

    JSON.parse()用于从一个字符串中解析出json对象. var str = '{"name":"huangxiaojian","age&quo ...

  6. 使用 dynamic 标记解析JSON字符串 JDynamic :支持Json反序列化为Dynamic对象

    使用 dynamic 标记解析JSON字符串  http://www.cnblogs.com/taotaodetuer/p/4171327.html 1 string jsonStr = " ...

  7. 字串符相关 split() 字串符分隔 substring() 提取字符串 substr()提取指定数目的字符 parseInt() 函数可解析一个字符串,并返回一个整数。

    split() 方法将字符串分割为字符串数组,并返回此数组. stringObject.split(separator,limit) 我们将按照不同的方式来分割字符串: 使用指定符号分割字符串,代码如 ...

  8. jq 解析josn字符串

    1. var obj = jQuery.parseJSON("${ruleModel.rules}"); 2. var obj = eval("("+" ...

  9. json解析json字符串时候,数组必须对应jsonObjectArray,不能对应JsonObject。否则会解析错误。

    json第三方解析json字符串时候,json数组必须对应jsonObjectArray,不能对应JsonObject.->只要是[]开头的都是json数组字符串,就要用jsonArray解析 ...

随机推荐

  1. Google Tango Java SDK开发:Configure and Connect 配置和连接

    Configure and Connect 配置和连接 Note: This section assumes you are familiar with the Android Activity Li ...

  2. Vivado&ISE&Quartus II调用Modelsim级联仿真

    博主一直致力寻找高效的工作方式,所以一直喜欢折腾软件,从刚开始只用软件IDE自带的编辑器,到Notepad++,再到后来的Vim,从用ISE14.7自带的Isim仿真,到发现更好的Modelsim,再 ...

  3. handsontable-developer guide-cell editor

    单元格编辑 cell editor renderer:展示数据:editor:改变数据:renderer用一个函数表示:后者有一系列的操作,需要用class来表示: EditorManager han ...

  4. IPv4&&IPv6地址结构分析

    IPv4套接字地址结构: 套接字都需要有一个指向套接字地址结构的指针作为参数.每个协议簇都定义它自己的套接字地址结构.这些结构的名字均已sockaddr_开头,并以对应每个协议族的唯一后缀结尾. wi ...

  5. [Elixir006]CSV(Comma-separated values)处理

    1. CSV文件格式是什么 CSV有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本).纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数 ...

  6. 销售系统项目业务分析和Java中使用邮箱

    项目一般大致可分为三个模块, 我们以销售系统为例 分为 基础模块 进货模块 财务模块三个 基础模块分为:权限模块 产品模块和基础代码,基础模块的设计十分重要会影响到整个项目, 代码较为简单 核心模块 ...

  7. Gogland使用 - 非常简单查看Go语言源代码全貌!

    Go语言也支持面向对象开发,不过和以往我们所使用的面向对象开发还是有不同,Go语言主张组合方式形成类的概念,在Go语言中,结构起到很大作用,如果用结构组合字段和方法,那么单纯在源代码中看,真的是费时费 ...

  8. 打开win8及以上操作系统的系统已安装程序目录

    Windows 8 的“Metro 界面”里不能像XP和Win7那样,点击“开始”->“程序”,显示系统所有安装的程序,这个功能还是非常有用的,可以帮助我们快速查看系统已经安装的程序!我编写了这 ...

  9. java 实验5 图形用户界面设计试验

    常用布局 1).流布局: FlowLayout 从左到右,自上而下方式在容器中排列,控件的大小不会随容器大小变化. 容器.setLayout(new FlowLayout(FlowLayout.LEF ...

  10. “全栈2019”Java多线程第二章:创建多线程之继承Thread类

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...