专注收集记录技术开发学习笔记、技术难点、解决方案
网站信息搜索 >> 请输入关键词:
您当前的位置: 首页 > Go

使用 Go 的 struct tag 回解析版本号字符串

发布时间:2011-06-29 17:50:49 文章来源:www.iduyao.cn 采编人员:星星草
使用 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"`
}
  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

友情提示:
信息收集于互联网,如果您发现错误或造成侵权,请及时通知本站更正或删除,具体联系方式见页面底部联系我们,谢谢。

其他相似内容:

  • ModernUI课程:定义一个Logo

    ModernUI教程:定义一个Logo ModernWindow的标题栏包含了一块区域用来显示自定义的窗体Logo: 这个窗体logo通过ModernWindow.LogoD...

  • Django忘记管理员账号和密码的解决方法

    Django忘记管理员账号和密码的解决办法 看着Django的教程学习搭建网站,结果忘记第一次创建的账号和密码了。结果搭建成功以后,一直...

  • GO语言小结(1)——基本知识

    GO语言总结(1)——基本知识 1、注释(与C++一样)   行注释://  块注释:/*   ...  */ 2、标识符   可以这么说,除了数字开头...

  • golang 惯用的文件读取方式

    golang 常用的文件读取方式 Golang 的文件读取方法很多,刚上手时不知道怎么选择,所以贴在此处便后速查。 一次性读取 小文件推荐一...

  • 查询深圳市通相关信息

    查询深圳通相关信息 用 HTTP.GET 从开放 API 中查询深圳通信息,然后将 JSON 数据存入结构体中,再格式化输出。 注意:获取的并不是实...

  • Go语言设计模式实践:结合(Composite)

    Go语言设计模式实践:组合(Composite) 关于本系列 这个系列首先是关于Go语言实践的。在项目中实际使用Go语言也有段时间了,一个体会就...

  • 列出索引和遍历目录

    列出目录和遍历目录 获取目录列表用 ioutil.ReadDir(),遍历目录用 filepath.Walk(),使用方法请参考文章示例。 示例代码: package ma...

  • io 包的惯用接口速记

    io 包的常用接口速记 我没有 C/C++ 基础,没有接口的概念,且从 Python 投奔而来,Python 的极简主义(一个结果往往只提供一个方法),让我在...

  • 代理服务扩充

    代理服务扩展 之前自己实现了一个代理服务,当时考虑的是只要支持SOCKS5就好了,因为我经常用CHROME,配合着SwitchySharp,体验还是很棒...

  • 文件的创造与打开

    文件的创建与打开 文件操作是个很重要的话题,使用也非常频繁,熟悉如何操作文件是必不可少的。Golang 对文件的支持是在 os package ...

热门推荐: