Skip to content

tinyhubs/properties

Repository files navigation

Overview

Build Status GoDoc Language License codecov goreport

*.properties文件是java里面很常见的配置文件。这里是一个go语言版的*.properties文件读处理库。本库支持properties文件的读取、修改、回写操作。也支持向properties文件中的属性追加、删除注释操作。

go-properties文件格式定义

为了使得properties文件的识别更加简单快速,go的properties的文件格式和java的properties文件并不是等价的。它将java里面一些很少用到的格式特性都去掉了。

golang版本的properties文件的格式定义如下:

  • 一行如果第一个非空白字符是#或者!,那么整行将被识别为注释行,注释行将被解析器忽略。

    比如,下面三行都会被解析器忽略(第三行是空白行):

    #  这是注释行
    !  这也是注释行
        
    
  • 每个配置项都是单行的key-value对,不支持跨行,key和value以=或者:分隔。

    比如,下面其实是三个配置项----UserName=Fabirc \Boch=Contry=US

    UserName = Fabirc \
    Boch
    Contry=US
    
  • key和value都是区分大小写;

    比如,下面其实是三个不同的配置项:

    SizeRange=1-20
    sizerange=1-20
    SIZERANGE=1-20
    
  • 一行的第一个=即使key与value的分隔字符,所以key中不会出现=,但value部分可以出现=

    比如,下面这个第一个行的key为expr,value是A-B=C;而第二行的key是一个""(空字符串),value是Hello。当然第二种情况并没有实际意义:

    expr=A-B=C
    =Hello
    
  • key和value的前后的空白都将被忽略,但key和value中间的空白会被原样保留;

    比如,下面这三个配置项的值都是1-20

      SizeRange-1=1-20
    SizeRange-2 =  1-20   
       SizeRange-3  =  1-20  
    
  • properties文件只支持**UTF-8**字符集,所以value中可以直接输入中文,遇到中文字符不必像java那样使用\uxxxx转义,直接用中文字面文字即可;

    比如,下面这个配置项的key为地址,value为深圳

      地址=深圳
    
  • 当value为空时,等号可忽略;

    所以,下面三个配置项的值都是空字符串(第一个等号后面有个):

    Address-1=  
    Address-2=
    Address-3
    

接口定义

属性文档

一个properties文档由一个properties.PropertiesDocument对象来表示。一个properties文档由多个key-value形式的属性组成。每个属性还可以追加一行或者多行注释。

加载属性文档

properties.Load 从io流生成一个properties.PropertiesDocument对象。

file, err := os.Open("test1.properties")
if nil != err {
    return
}

doc, err := properties.Load(file)
if nil != err {
    t.Error("加载失败")
    return
}

fmt.Println(doc.String("key"))

创建一个新的属性文档对象

properties.New 直接创建一个新的属性文档对象,常用于属性创建文档文件的场景下。我们随后可以通过properties.Save函数将属性文档写入到文件或者输出流。

doc := properties.New()
doc.Set("a", "aaa")
doc.Comment("a", "This is a comment for a")

buf := bytes.NewBufferString("")
properties.Save(doc, buf)

将属性回写到文件或者流

properties.Save 可用于将一个文档回写到指定的writer中去。

buf := bytes.NewBufferString("")
properties.Save(doc, buf)

属性值的读取

  • 通用读取能力

PropertiesDocument对象的Get方法提供了一个基本的元素读取能力:

func (p PropertiesDocument) Get(key string) (value string, exist bool)

Get函数会返回两个参数,当对应的key在PropertiesDocument文档中存在时,会返回该key对应的value,且exist的值将为true;如果不存在,exist的值将是false。

我们经常利用Get来探测,某个指定key的属性是否在属性文件中定义了。

  • 读取并转换 读取属性然后转成对应的数据类型是个很常见的任务,所以PropertiesDocument为最常用的几种类型提供了方便的读取并转换的函数。

    • String() 读取一个字符串型的属性,如果不存在默认返回""
    • Int() 读取一个属性并转换为int64类型,如果key对应的属性不存在,或者转换失败,返回值为0
    • Uint()Int()函数类似,只是返回的数据类型为uint64
    • Float() 也是和Int()函数类似,但返回值为float64
    • Bool() 同与String类似,只是返回值是bool类型的且缺省值是falseBool函数会将1, t, T, true, TRUE, True识别为true,将0, f, F, false, FALSE, False识别为false
    • Object 这个函数提供了一个数据映射能力,可以将找到的value映射为任何类型。
  • 指定读取的缺省值 前面的String()Int()等函数在key不存在或者抓换失败的场景下,默认会返回零值。但零值往往不能满足我们的诉求,我们经常需要自己指定这些场景下的返回值。StringDefaultIntDefaultFloatDefaultBoolDefaultObjectDefault 这几个函数的返回值和前面不带Default后缀的函数的行为类似,只是当配置项不存在时或者数据格式错误时,会直接返回参数中的def(缺省值)。

属性的增删改

  • 增加或者修改属性

Set()函数用于修改指定的key的属性的值。如果指定的key的属性不存在,那么自动创建一个。

本库并没有提供按类型设置属性值的功能,前面描述的Set()函数只接受字符串类型的属性值作为输入。主要原因是数据的转换方式非常多,没有一种普适 的数据转换方法。所以,对于非字符串类型的值的设置需要自行转换成string类型。

doc.Set("key", "value")
  • 处理属性不存在的场景

当属性不存在时,Set()函数会新建一个属性值,这种工作方式通常是很有用的。但是,有时候我们不希望Set()的这种自作聪明的行为。此时,我们可以通过Get()方法判断一些以确定是否需调用Set()

_, exist := doc.Get("key")
if !exist {
    return errors.New("Key is not exist")
}

doc.Set("key", "New-Value")
  • 删除属性

Del()函数用于删除指定key的属性。它会返回一个bool值,用于表示当前的key的属性是否存在。

exist := doc.Del("key")

操作注释

在本库中,注释是绑定到属性的。位于属性的key-value定义前面,且与属性之间没有空白行的多行注释,我们会判定这些注释是属于该属性的,比如:

 # Comment1
 # Comment2
 
 # Comment3
 # Comment4
 mykey=myvalue

上面的Comment3和Comment4是mykey属性的注释,但是Comment1和Comment2却不是。

PropertiesDocument的Comment()函数用于为属性指定一些注释。而Uncomment()函数用于删除指定的key的注释。

PropertiesDocument的Comment()函数允许一次性指定多行注释,而Uncomment()用于一次性删除一个指定的key的所有的注释。

文档对象枚举

PropertiesDocument的Accept()Foreach()函数都是用来对文档对象进行枚举的,但是Foreach()专用于对属性进行遍历。而Accept()可以通过对属性和注释进行遍历。

实际上,Save()函数就是利用Accept()函数来实现的:

func Save(doc *PropertiesDocument, writer io.Writer) {
    doc.Accept(func(typo byte, value string, key string) bool {
        switch typo {
        case '#', '!', ' ':
            {
                fmt.Fprintln(writer, value)
            }
        case '=', ':':
            {
                fmt.Fprintf(writer, "%s%c%s\n", key, typo, value)
            }
        }
        return true
    })
}

Accept()函数的回调函数里面有个typo参数,这个参数决定了当前这条记录是注释还是一个有效的属性。typo的取值可能有下面一些:

  • '#' 表示当前的value是#开头的注释
  • '!' 表示当前的value是!开头的注释
  • ' ' 表示当前的value是个空行或者空白行
  • '=' 表示当前的value是个以=分隔的属性
  • ':' 表示当前的value是个以:分隔的属性

Releases

No releases published

Packages

No packages published

Languages