使用 Go 的 struct tag 来解析版本号字符串
各类软件的版本号定义虽然都不尽相同,但是其基本原理基本上还是相通的:通过特写的字符对字符串进行分割。我们把这一规则稍作整理,放到 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"`
}- 其中 struct tag 中的第一段内容表示的是当前字段的一个编号,要求唯一且为数值,0 表示入口;
- 后面的是解析规则,可以有多条,以逗号分隔,优先级等同;
- 每一条规则的第一个字符为触发条件,之后的数字即为前面的编号,当解析器碰到该字符时,即结束当前字段的解析,跳转到其后面指定的编号字段。
如何实现
首先定义一个表示每个字段的结构:
 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")
使用 Go 的 struct tag 来解析版本号字符串的更多相关文章
- 【linux】U-BOOT与linux kernel通信: struct tag
		欢迎转载,转载时需保留作者信息. 邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http://b ... 
- 引用 U-boot给kernel传参数和kernel读取参数—struct tag
		引用 清风徐徐 的 U-boot给kernel传参数和kernel读取参数—struct tag U-boot会给Linux Kernel传递很多参数,如:串口,RAM,videofb等.而Linux ... 
- Go语言中的struct tag
		有时在Go的结构体定义时会看到这样的形式: type User struct { UserId int `json:"user_id" bson:"b_user_id&q ... 
- java后台处理解析json字符串的两种方式
		简单说一下背景 上次后端通过模拟http请求百度地图接口,得到的是一个json字符串,而我只需要其中的某个key对应的value. 当时我是通过截取字符串取的,后来觉得不太合理,今天整理出了两种处理解 ... 
- js中解析json对象:JSON.parse()用于从一个字符串中解析出json对象, JSON.stringify()用于从一个对象解析出字符串。
		JSON.parse()用于从一个字符串中解析出json对象. var str = '{"name":"huangxiaojian","age&quo ... 
- 使用 dynamic 标记解析JSON字符串 JDynamic :支持Json反序列化为Dynamic对象
		使用 dynamic 标记解析JSON字符串 http://www.cnblogs.com/taotaodetuer/p/4171327.html 1 string jsonStr = " ... 
- 字串符相关 split() 字串符分隔  substring() 提取字符串  substr()提取指定数目的字符 parseInt() 函数可解析一个字符串,并返回一个整数。
		split() 方法将字符串分割为字符串数组,并返回此数组. stringObject.split(separator,limit) 我们将按照不同的方式来分割字符串: 使用指定符号分割字符串,代码如 ... 
- jq 解析josn字符串
		1. var obj = jQuery.parseJSON("${ruleModel.rules}"); 2. var obj = eval("("+" ... 
- json解析json字符串时候,数组必须对应jsonObjectArray,不能对应JsonObject。否则会解析错误。
		json第三方解析json字符串时候,json数组必须对应jsonObjectArray,不能对应JsonObject.->只要是[]开头的都是json数组字符串,就要用jsonArray解析 ... 
随机推荐
- Deep learning for visual understanding: A review
			https://www.sciencedirect.com/science/article/pii/S0924271618301291?dgcid=raven_sd_recommender_email ... 
- SqlLocalDB 的一些常用命令行
			Once installed, you can interact with SqlLocalDb using the command line. The following will tell you ... 
- [转载]关于JAVA中子类和父类的构造方法
			原文链接:https://www.cnblogs.com/chenpi/p/5486096.html#_label0 
- 工作中经常打交道的Java代码の容器(一)
- linux常见命令-查看磁盘空间
			linux查看磁盘使用情况命令 1. 统一每个目录下磁盘的整体情况: df -h 2. 查看指定目录,在命令后直接放目录名,比如查看“usr”目录使用情况:df -h /usr/ 3. 查看当前目录 ... 
- sql2008 获取表结构说明
			SELECT 表名 = case when a.colorder=1 then d.name else '' end, 表说明 = case when a.color ... 
- Hadoop 新建集群namenode format
			在hadoop部署好了之后是不能马上应用的,还要对配置的文件系统进行格式化. 使用命令: hadoop namenode -format 注释:namenode和secondary namenode均 ... 
- linux清理磁盘
			https://blog.csdn.net/u012660464/article/details/78923011 有时候,服务突然挂了,再次启动却启动不了.一看,原来是磁盘空间被占满啦,那么,怎么清 ... 
- 搭建 .NET Core 开发环境
			安装 .Net Core 执行代码 任务时间:时间未知 .NET Core 的官方文档很详细,本实验带你建立一个.NET Core 1.1的Web运行环境,更多内容可以可以查阅微软官方文档. 安装 . ... 
- Robolectric 单元测试中使用 Ressource
			单元测试类中: @RunWith(RobolectricGradleTestRunner.class) @Config(constants=BuildConfig.class, sdk = 21) 获 ... 
