README ¶
ini
Package ini provides INI file read and write functionality in Go.
Feature
- Load multiple data sources(
[]byte
or file) with overwrites. - Read with recursion values.
- Read with parent-child sections.
- Read with auto-increment key names.
- Read with multiple-line values.
- Read with tons of helper methods.
- Read and convert values to Go types.
- Read and WRITE comments of sections and keys.
- Manipulate sections, keys and comments with ease.
- Keep sections and keys in order as you parse and save.
Installation
go get gopkg.in/ini.v1
Getting Started
Loading from data sources
A Data Source is either raw data in type []byte
or a file name with type string
and you can load as many as data sources you want. Passing other types will simply return an error.
cfg, err := ini.Load([]byte("raw data"), "filename")
Or start with an empty object:
cfg := ini.Empty()
When you cannot decide how many data sources to load at the beginning, you still able to Append() them later.
err := cfg.Append("other file", []byte("other raw data"))
Working with sections
To get a section, you would need to:
section, err := cfg.GetSection("section name")
For a shortcut for default section, just give an empty string as name:
section, err := cfg.GetSection("")
When you're pretty sure the section exists, following code could make your life easier:
section := cfg.Section("")
What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you.
To create a new section:
err := cfg.NewSection("new section")
To get a list of sections or section names:
sections := cfg.Sections()
names := cfg.SectionStrings()
Working with keys
To get a key under a section:
key, err := cfg.Section("").GetKey("key name")
Same rule applies to key operations:
key := cfg.Section("").Key("key name")
To create a new key:
err := cfg.Section("").NewKey("name", "value")
To get a list of keys or key names:
keys := cfg.Section().Keys()
names := cfg.Section().KeyStrings()
To get a clone hash of keys and corresponding values:
hash := cfg.GetSection("").KeysHash()
Working with values
To get a string value:
val := cfg.Section("").Key("key name").String()
To get value with types:
// For boolean values:
// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On
// false when value is: 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off
v, err = cfg.Section("").Key("BOOL").Bool()
v, err = cfg.Section("").Key("FLOAT64").Float64()
v, err = cfg.Section("").Key("INT").Int()
v, err = cfg.Section("").Key("INT64").Int64()
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
v = cfg.Section("").Key("BOOL").MustBool()
v = cfg.Section("").Key("FLOAT64").MustFloat64()
v = cfg.Section("").Key("INT").MustInt()
v = cfg.Section("").Key("INT64").MustInt64()
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
// Methods start with Must also accept one argument for default value
// when key not found or fail to parse value to given type.
// Except method MustString, which you have to pass a default value.
v = cfg.Seciont("").Key("String").MustString("default")
v = cfg.Section("").Key("BOOL").MustBool(true)
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
v = cfg.Section("").Key("INT").MustInt(10)
v = cfg.Section("").Key("INT64").MustInt64(99)
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
What if my value is three-line long?
[advance]
ADDRESS = """404 road,
NotFound, State, 5000
Earth"""
Not a problem!
cfg.Section("advance").Key("ADDRESS").String()
/* --- start ---
404 road,
NotFound, State, 5000
Earth
------ end --- */
That's all? Hmm, no.
Helper methods of working with values
To get value with given candidates:
v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates.
To validate value in a given range:
vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
To auto-split value into slice:
vals = cfg.Section("").Key("STRINGS").Strings(",")
vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
vals = cfg.Section("").Key("INTS").Ints(",")
vals = cfg.Section("").Key("INT64S").Int64s(",")
vals = cfg.Section("").Key("TIMES").Times(",")
Advanced Usage
Recursive Values
For all value of keys, there is a special syntax %(<name>)s
, where <name>
is the key name in same section or default section, and %(<name>)s
will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions.
NAME = ini
[author]
NAME = Unknwon
GITHUB = https://github.com/%(NAME)s
[package]
FULL_NAME = github.com/go-ini/%(NAME)s
cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
Parent-child Sections
You can use .
in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section.
NAME = ini
VERSION = v1
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
Auto-increment Key Names
If key name is -
in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter.
[features]
-: Support read/write comments of keys and sections
-: Support auto-increment of key names
-: Support load multiple files to overwrite key values
cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
Map To Struct
Want more objective way to play with INI? Cool.
Name = Unknwon
age = 21
Male = true
Born = 1993-01-01T20:17:05Z
[Note]
Content = Hi is a good man!
Cities = HangZhou, Boston
type Note struct {
Content string
Cities []string
}
type Person struct {
Name string
Age int `ini:"age"`
Male bool
Born time.Time
Note
Created time.Time `ini:"-"`
}
func main() {
cfg, err := ini.Load("path/to/ini")
// ...
p := new(Person)
err = cfg.MapTo(p)
// ...
// Things can be simpler.
err = ini.MapTo(p, "path/to/ini")
// ...
// Just map a section? Fine.
n := new(Note)
err = cfg.Section("Note").MapTo(n)
// ...
}
Can I have default value for field? Absolutely.
Assign it before you map to struct. It will keep the value as it is if the key is not presented or got wrong type.
// ...
p := &Person{
Name: "Joe",
}
// ...
Name Mapper
To save your time and make your code cleaner, this library supports NameMapper
between struct field and actual secion and key name.
There are 2 built-in name mappers:
AllCapsUnderscore
: it converts to formatALL_CAPS_UNDERSCORE
then match section or key.TitleUnderscore
: it converts to formattitle_underscore
then match section or key.
To use them:
type Info struct{
PackageName string
}
func main() {
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("packag_name=ini"))
// ...
cfg, err := ini.Load("PACKAGE_NAME=ini")
// ...
info := new(Info)
cfg.NameMapper = ini.AllCapsUnderscore
err = cfg.MapTo(info)
// ...
}
Getting Help
FAQs
What does BlockMode
field do?
By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set cfg.BlockMode = false
to speed up read operations about 50-70% faster.
Why another INI library?
Many people are using my another INI library goconfig, so the reason for this one is I would like to make more Go style code. Also when you set cfg.BlockMode = false
, this one is about 10-30% faster.
To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using gopkg.in
to version my package at this time.(PS: shorter import path)
License
This project is under Apache v2 License. See the LICENSE file for the full license text.
Documentation ¶
Overview ¶
Package ini provides INI file read and write functionality in Go.
Index ¶
- Constants
- Variables
- func MapTo(v, source interface{}, others ...interface{}) error
- func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error
- func Version() string
- type File
- func (f *File) Append(source interface{}, others ...interface{}) error
- func (f *File) DeleteSection(name string)
- func (f *File) GetSection(name string) (*Section, error)
- func (f *File) MapTo(v interface{}) error
- func (f *File) NewSection(name string) (*Section, error)
- func (f *File) NewSections(names ...string) (err error)
- func (f *File) Reload() error
- func (f *File) SaveTo(filename string) (err error)
- func (f *File) Section(name string) *Section
- func (f *File) SectionStrings() []string
- func (f *File) Sections() []*Section
- type Key
- func (k *Key) Bool() (bool, error)
- func (k *Key) Float64() (float64, error)
- func (k *Key) Float64s(delim string) []float64
- func (k *Key) In(defaultVal string, candidates []string) string
- func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64
- func (k *Key) InInt(defaultVal int, candidates []int) int
- func (k *Key) InInt64(defaultVal int64, candidates []int64) int64
- func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time
- func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time
- func (k *Key) Int() (int, error)
- func (k *Key) Int64() (int64, error)
- func (k *Key) Int64s(delim string) []int64
- func (k *Key) Ints(delim string) []int
- func (k *Key) MustBool(defaultVal ...bool) bool
- func (k *Key) MustFloat64(defaultVal ...float64) float64
- func (k *Key) MustInt(defaultVal ...int) int
- func (k *Key) MustInt64(defaultVal ...int64) int64
- func (k *Key) MustString(defaultVal string) string
- func (k *Key) MustTime(defaultVal ...time.Time) time.Time
- func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time
- func (k *Key) Name() string
- func (k *Key) RangeFloat64(defaultVal, min, max float64) float64
- func (k *Key) RangeInt(defaultVal, min, max int) int
- func (k *Key) RangeInt64(defaultVal, min, max int64) int64
- func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time
- func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time
- func (k *Key) SetValue(v string)
- func (k *Key) String() string
- func (k *Key) Strings(delim string) []string
- func (k *Key) Time() (time.Time, error)
- func (k *Key) TimeFormat(format string) (time.Time, error)
- func (k *Key) Times(delim string) []time.Time
- func (k *Key) TimesFormat(format, delim string) []time.Time
- func (k *Key) Value() string
- type NameMapper
- type Section
- func (s *Section) DeleteKey(name string)
- func (s *Section) GetKey(name string) (*Key, error)
- func (s *Section) Key(name string) *Key
- func (s *Section) KeyStrings() []string
- func (s *Section) Keys() []*Key
- func (s *Section) KeysHash() map[string]string
- func (s *Section) MapTo(v interface{}) error
- func (s *Section) Name() string
- func (s *Section) NewKey(name, val string) (*Key, error)
Constants ¶
const (
DEFAULT_SECTION = "DEFAULT"
)
Variables ¶
var ( LineBreak = "\n" // Write spaces around "=" to look better. PrettyFormat = true )
Functions ¶
func MapTo ¶
func MapTo(v, source interface{}, others ...interface{}) error
MapTo maps data sources to given struct.
func MapToWithMapper ¶
func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error
MapTo maps data sources to given struct with name mapper.
Types ¶
type File ¶
type File struct { // Should make things safe, but sometimes doesn't matter. BlockMode bool NameMapper // contains filtered or unexported fields }
File represents a combination of a or more INI file(s) in memory.
func Load ¶
Load loads and parses from INI data sources. Arguments can be mixed of file name with string type, or raw data in []byte.
func (*File) DeleteSection ¶
DeleteSection deletes a section.
func (*File) GetSection ¶
GetSection returns section by given name.
func (*File) NewSection ¶
NewSection creates a new section.
func (*File) NewSections ¶
NewSections creates a list of sections.
func (*File) SectionStrings ¶
SectionStrings returns list of section names.
type Key ¶
type Key struct { Comment string // contains filtered or unexported fields }
Key represents a key under a section.
func (*Key) In ¶
In always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InFloat64 ¶
InFloat64 always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InInt ¶
InInt always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InInt64 ¶
InInt64 always returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InTime ¶
InTime always parses with RFC3339 format and returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) InTimeFormat ¶
InTimeFormat always parses with given format and returns value without error, it returns default value if error occurs or doesn't fit into candidates.
func (*Key) MustBool ¶
MustBool always returns value without error, it returns false if error occurs.
func (*Key) MustFloat64 ¶
MustFloat64 always returns value without error, it returns 0.0 if error occurs.
func (*Key) MustString ¶
MustString returns default value if key value is empty.
func (*Key) MustTime ¶
MustTime always parses with RFC3339 format and returns value without error, it returns zero value if error occurs.
func (*Key) MustTimeFormat ¶
MustTimeFormat always parses with given format and returns value without error, it returns zero value if error occurs.
func (*Key) RangeFloat64 ¶
RangeFloat64 checks if value is in given range inclusively, and returns default value if it's not.
func (*Key) RangeInt ¶
RangeInt checks if value is in given range inclusively, and returns default value if it's not.
func (*Key) RangeInt64 ¶
RangeInt64 checks if value is in given range inclusively, and returns default value if it's not.
func (*Key) RangeTime ¶
RangeTime checks if value with RFC3339 format is in given range inclusively, and returns default value if it's not.
func (*Key) RangeTimeFormat ¶
RangeTimeFormat checks if value with given format is in given range inclusively, and returns default value if it's not.
func (*Key) TimeFormat ¶
TimeFormat parses with given format and returns time.Time type value.
func (*Key) Times ¶
Times parses with RFC3339 format and returns list of time.Time devide by given delimiter.
func (*Key) TimesFormat ¶
TimesFormat parses with given format and returns list of time.Time devide by given delimiter.
type NameMapper ¶
NameMapper represents a ini tag name mapper.
var ( // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. AllCapsUnderscore NameMapper = func(raw string) string { newstr := make([]rune, 0, len(raw)) for i, chr := range raw { if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { if i > 0 { newstr = append(newstr, '_') } } newstr = append(newstr, unicode.ToUpper(chr)) } return string(newstr) } // TitleUnderscore converts to format title_underscore. TitleUnderscore NameMapper = func(raw string) string { newstr := make([]rune, 0, len(raw)) for i, chr := range raw { if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { if i > 0 { newstr = append(newstr, '_') } chr -= ('A' - 'a') } newstr = append(newstr, chr) } return string(newstr) } )
Built-in name getters.
type Section ¶
type Section struct { Comment string // contains filtered or unexported fields }
Section represents a config section.
func (*Section) KeyStrings ¶
KeyStrings returns list of key names of section.