Article from
Quick Start
my.ini # possible values : production, development app_mode = development [paths] # Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) data = /home/git/grafana [server] # Protocol (http or https) protocol = http # The http port to use http_port = 9999 # Redirect to correct domain if host header does not match domain # Prevents DNS rebinding attacks enforce_domain = true
Resolve
main Package Import ( "FMT" "OS" "gopkg.in/ini.v1" ) FUNC main () { CFG, ERR: = ini.Load ( "the my.ini") ! = nil {IF ERR fmt.Printf ( "read the Fail to File:% V", ERR) os.Exit (. 1) } // typical read operation, the default partition can use an empty string indicates fmt.Println ( "App Mode:", cfg.Section ( "" ) .Key ( "app_mode") String ()). fmt.Println ( "the Data Path:."., cfg.Section ( "Paths") Key ( "the Data") String ()) // we can do some candidates value limiting operation fmt.Println ( "Protocol Server:", cfg.Section ( "Server") Key ( "Protocol") the in ( "HTTP",..[] {String "HTTP", "HTTPS"})) // if the read value is not within the candidate list, the default value will fallback using the provided fmt.Println ( "Email Protocol:", cfg.Section ( "Server"). Key ( "Protocol"). the In ( "SMTP", [] {String "IMAP", "SMTP"})) // try automatic conversion fmt.Printf ( "Port Number The: (% [. 1] T)% [. 1] D \ n-", cfg.Section (" Server ") (Http_port") MustInt (9999)). Key. " fmt.Printf (" the Enforce the Domain: (% [ 1] T)% [1] v \ the n-", cfg.Section (" Server "). Key (" enforce_domain "). MustBool (false)) // about it, change certain values and then save cfg.Section ( "") .Key ( "app_mode"). the SetValue ( "Production") cfg.SaveTo ( "my.ini.local") }
result
$ go run main.go App Mode: development Data Path: /home/git/grafana Server Protocol: http Email Protocol: smtp Port Number: (int) 9999 Enforce Domain: (bool) true $ cat my.ini.local # possible values : production, development app_mode = production [paths] # Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) data = /home/git/grafana ...
Loaded from the data source
As said before, load the configuration data from multiple sources is a basic operation.
So, in the end what is the data source it?
Data source can be a []byte
type of raw data string
type or file path io.ReadCloser
. You can load any number of data sources. If you pass other types of data sources, it will directly return an error.
CFG, ERR: = ini.Load ( [] byte ( "RAW Data"), original data // "filename", // File Path ioutil.NopCloser (bytes.NewReader ([] byte ( "some other data")) ), )
Or start with a blank document:
cfg: = ini.Empty ()
When you can not decide at the outset what data source to load, you can still use the Append () to load them when needed.
err := cfg.Append("other file", []byte("other raw data"))
When you want to load a series of documents, but can not determine which file does not exist, you can ignore them by calling the function LooseLoad ().
cfg, err := ini.LooseLoad("filename", "filename_404")
More Niubi, when those documents did not previously exist suddenly appeared at the time to recall Reload () method, then they will be properly loaded.
Data overwritten
When loading a plurality of data sources, if a certain key appears in one or more data sources, the data will overwrite occurs. A previous value of the key source data will be read the next data source overwritten.
For example, if the two loading profiles my.ini
and my.ini.local
( started with the file input and output), app_mode
the value is production
not development
.
cfg, err := ini.Load("my.ini", "my.ini.local") ... cfg.Section("").Key("app_mode").String() // production
Only overwrite the data does not trigger in one case, that the use ShadowLoad load data source.
In some cases, your configuration file may contain a non-key-value pairs of data lines, the parser will complain default and terminate resolution. If you want to be able to ignore the parser and parse them to complete the remaining content can be achieved by the following method:
cfg, err := ini.LoadSources(ini.LoadOptions{ SkipUnrecognizableLines: true, }, "other.ini")
Save arrangement
And finally to this moment, it is time to save the configuration.
More primitive approach is to configure the output to a file:
// ... err = cfg.SaveTo("my.ini") err = cfg.SaveToIndent("my.ini", "\t")
Another more advanced approach is to write to implement any io.Writer
object interface in:
// ... cfg.WriteTo(writer) cfg.WriteToIndent(writer, "\t")
By default, the spaces between the equal sign will be used to align the key to beautify the output, the following code can disable the feature:
ini.PrettyFormat = false
Partition Operation (Section)
Gets the specified partition:
sec, err := cfg.GetSection("section name")
If you want to get the default partition, you can replace with an empty string partition name:
sec, err := cfg.GetSection("")
Correspondingly, it can also be used ini.DEFAULT_SECTION
to get the default partition:
sec, err := cfg.GetSection(ini.DEFAULT_SECTION)
When you are pretty sure a partition exists, you can use the following simple method:
sec := cfg.Section("section name")
If you accidentally misjudged, to get the partition in fact does not exist, what would happen then? Never mind, it will automatically create and return a corresponding partition object to you.
Create a partition:
err := cfg.NewSection("new section")
Get all partitioned object or name:
secs := cfg.Sections() names := cfg.SectionStrings()
Father and son reading a partition
You can use the partition name .
to represent the parent-child relationship between two or more partitions. If a key does not exist in a child partition, it will go looking for parent partition again until no parent partition
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
Unable to resolve partition
If you have some special partition, they do not contain common key-value pairs, but there is no fixed format, plain text, you can use LoadOptions.UnparsableSections
the following conditions:
cfg, err := ini.LoadSources(ini.LoadOptions{ UnparseableSections: []string{"COMMENTS"}, }, `[COMMENTS] <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`) body := cfg.Section("COMMENTS").Body() /* --- start --- <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> ------ end --- */
Operation key (Key)
Get key under a partition:
key, err := cfg.Section("").GetKey("key name")
And partitions as you can get direct bond while ignoring error handling:
key := cfg.Section("").Key("key name")
Determine if a key is present:
yes := cfg.Section("").HasKey("key name")
Get all keys or key names in the partition:
keys := cfg.Section("").Keys() names := cfg.Section("").KeyStrings()
Get all clones of key-value pairs in the partition:
hash := cfg.Section("").KeysHash()
Ignore case key names
Sometimes the name of the partition and keys in mixed case is very annoying, this time by InsensitiveLoad all partitions and key names in the cast read in lowercase
cfg, ERR: = ini.InsensitiveLoad ( "filename") // ... // SEC1 and sec2 point to the same partition object SEC1, ERR: = cfg.GetSection ( "Section") sec2, ERR: = cfg.GetSection ( "sectionTop") // key1 and key2 objects point to the same key key1, ERR: = sec1.GetKey ( "key") key2, ERR: = sec2.GetKey ( "kEY")
MySQL configuration similar to Boolean key
MySQL configuration file boolean key no specific values occur:
[mysqld] ... skip-host-cache skip-name-resolve
By default, this is considered to be missing values can not be completed resolve, but they can be processed by advanced loading options:
cfg, err := ini.LoadSources(ini.LoadOptions{ AllowBooleanKeys: true, }, "my.cnf")
Their values are forever true
, and when you save the file will only output the key name.
If you want to generate such key through the program, you can use NewBooleanKey
:
key, err := sec.NewBooleanKey("skip-host-cache")
With a key name that contains multiple values
Do you also been plagued by the following configuration file?
[remote "origin"] url = https://github.com/Antergone/test1.git url = https://github.com/Antergone/test2.git fetch = +refs/heads/*:refs/remotes/origin/*
Yes! By default, the value of only the last occurrence is saved in the url
middle, but I just want to keep the value of all how do ah? It does not matter, with ShadowLoad easily solve your troubles:
cfg, err := ini.ShadowLoad(".gitconfig") // ... f.Section(`remote "origin"`).Key("url").String() // Result: https://github.com/Antergone/test1.git f.Section(`remote "origin"`).Key("url").ValueWithShadows() // Result: []string{ // "https://github.com/Antergone/test1.git", // "https://github.com/Antergone/test2.git", // }
Read increment keys
If the data source named in the bond -
, is considered key to use a special syntax increment keys. Counter starts, and between the partitions are independent.
[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"}
Get all the keys under the superior parent partition
cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"]
Operation keys (Value)
Obtaining a value of type string (string) of:
val := cfg.Section("").Key("key name").String()
Get value while custom function verification process:
val := cfg.Section("").Key("key name").Validate(func(in string) string { if len(in) == 0 { return "default" } return in })
If you do not need any automatic change function values (such as recursive read), you can get the original value (in this way best performance) directly:
val := cfg.Section("").Key("key name").Value()
Determine whether there is an original value:
yes := cfg.Section("").HasValue("test value")
Other types of acquired values:
// 布尔值的规则: // true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On // false 当值为:0, f, F, FALSE, false, False, NO, no, No, n, 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("UINT").Uint() v, err = cfg.Section("").Key("UINT64").Uint64() 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() cfg.Section = V ( ""). Key ( "INT64"). MustInt64 () cfg.Section = V ( ""). Key ( "UINT"). MustUint () V = cfg.Section ( ""). Key ( "UINT64"). MustUint64 () V = cfg.Section ( ""). Key ( "the TIME"). MustTimeFormat (time.RFC3339) V = cfg.Section ( ""). Key ( "the TIME"). MustTime () // RFC3339 // method name preceded by a Must allow the receiving of the same type parameter as the default value, when the key is not present or when // conversion fails, it will directly return to the default values. // However, MustString method must pass a default value. cfg.Section = V ( ""). Key ( "String"). MustString ( "default") V = cfg.Section ( ""). Key ( "BOOL"). MustBool (to true) V = cfg.Section ( "") .Key ( "float64"). MustFloat64 (1.25) V = cfg.Section ( ""). Key ( "the INT"). MustInt (10) V = cfg.Section ( ""). Key ( "INT64 "). v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
I have a lot of value if the line how to do?
[advance] ADDRESS = """404 road, NotFound, State, 5000 Earth"""
Ah ha? Small case!
cfg.Section("advance").Key("ADDRESS").String() /* --- start --- 404 road, NotFound, State, 5000 Earth ------ end --- */
Chan burst! What if I write content that is less than one line written to the second line of how you want to do?
[advance] two_lines = how about \ continuation lines? lots_of_lines = 1 \ 2 \ 3 \ 4
It is simply a piece of cake!
cfg.Section("advance").Key("two_lines").String() // how about continuation lines? cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
But I sometimes feel special boring two lines together, how can we not automatically connect the two lines do?
cfg, err := ini.LoadSources(ini.LoadOptions{ IgnoreContinuation: true, }, "filename")
Holy crap to force ah!
Note that the value of both sides of the single quotation marks are automatically excluded:
foo = "some value" // foo: some value bar = 'some value' // bar: some value
Sometimes you get value like Crowdin files downloaded from the Web site as a special format (values using double quotes inside of double quotes are escaped):
create_repo="created repository <a href=\"%s\">%s</a>"
So, how such values are automatically deal with it?
cfg, err := ini.LoadSources(ini.LoadOptions{UnescapeValueDoubleQuotes: true}, "en-US.ini")) cfg.Section("<name of your section>").Key("create_repo").String() // You got: created repository <a href="%s">%s</a>
That's all? Haha, of course not.
The method of operation of the auxiliary keys
Setting the candidate values acquired key:
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("UINT").InUint(4, []int{3, 6, 9}) v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9}) 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
If any value is not a candidate to get the value of a will return the default value and the default value need not be a candidate in one.
Verification of the acquired value is within the specified 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("UINT").RangeUint(0, 3, 9) vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9) vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime) vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
Automatic segmentation key to slice (Slice)
When there is an invalid input, instead of a zero value:
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] // Input: how, 2.2, are, you -> [0.0 2.2 0.0 0.0] 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("UINTS").Uints(",") vals = cfg.Section("").Key("UINT64S").Uint64s(",") vals = cfg.Section("").Key("TIMES").Times(",")
Excluding results from an invalid input sections:
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] // Input: how, 2.2, are, you -> [2.2] vals = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",") vals = cfg.Section("").Key("INTS").ValidInts(",") vals = cfg.Section("").Key("INT64S").ValidInt64s(",") vals = cfg.Section("").Key("UINTS").ValidUints(",") vals = cfg.Section("").Key("UINT64S").ValidUint64s(",") vals = cfg.Section("").Key("TIMES").ValidTimes(",")
When there is an invalid entry, an error is returned directly to:
// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] // Input: how, 2.2, are, you -> error vals = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",") vals = cfg.Section("").Key("INTS").StrictInts(",") vals = cfg.Section("").Key("INT64S").StrictInt64s(",") vals = cfg.Section("").Key("UINTS").StrictUints(",") vals = cfg.Section("").Key("UINT64S").StrictUint64s(",") vals = cfg.Section("").Key("TIMES").StrictTimes(",")
Recursive read key
In the process of obtaining all keys, special syntax %(<name>)s
can be applied, which <name>
can be the same partition or default key name under the partition. String %(<name>)s
will be replaced by the corresponding key, if the specified key does not exist, it will be replaced with an empty string. You can use up to 99 layers of nested recursive.
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
Python multi-line value
If you are new to the service migrated from Python, you may experience some of the old syntax of the configuration file, do not panic!
cfg, err := ini.LoadSources(ini.LoadOptions{ AllowPythonMultilineValues: true, }, []byte(` [long] long_rsa_private_key = -----BEGIN RSA PRIVATE KEY----- foo bar foobar barfoo -----END RSA PRIVATE KEY----- `) /* -----BEGIN RSA PRIVATE KEY----- foo bar foobar barfoo -----END RSA PRIVATE KEY----- */
Note operation (it Comment)
The content of the following situations will be treated as a comment:
- All at
#
or;
lines that start with - All
#
or;
after the contents - Text (that is, after the partition label
[分区名]
after the contents)
If you want to use the included #
or ;
value, use `
or """
be coated.
In addition, you can also completely ignored within LoadOptions line comments:
cfg, err := ini.LoadSources(ini.LoadOptions{ IgnoreInlineComment: true, }, "app.ini")
Or before the comment symbol must be requested with a space:
cfg, err := ini.LoadSources(ini.LoadOptions{ SpaceBeforeInlineComment: true, }, "app.ini")
Mapped to the structure
I want to use a more object-oriented way Fun INI do? good idea.
Name = Unknwon age = 21 Male = true Born = 1993-01-01T20:17:05Z [Note] Content = Hi is a good man! Cities = HangZhou, Boston
Note {struct type the Content String Cities [] String } type struct {the Person the Name String Age int `INI:" Age "` for a Man BOOL Born time.time Note the Created time.time `INI:" - "` } FUNC main () { cfg, ERR: ini.Load = ( "path / to / the INI") // ... the p-: = new new (the Person) ERR = cfg.MapTo (the p-) // ... // everything could be so simple . = ini.MapTo ERR (the p-, "path / to / the INI") // ... // ah ha? You only need to map a partition? n-: = new new (Note) . cfg.Section ERR = ( "Note") MapTo (n-) // ... }
Field structure of how to set the default value of it? Very simple, as long as the assignment of the specified field before mapping it. If the key is not found or the wrong type, change the value it does not happen.
INI play this really cool ah! However, if you can not give me back the original configuration file, what with eggs?
From the structure of the reflective
type Embeded struct { Dates []time.Time `delim:"|" comment:"Time data"` Places []string `ini:"places,omitempty"` None []int `ini:",omitempty"` } type Author struct { Name string `ini:"NAME"` Male bool Age int `comment:"Author's age"` GPA float64 NeverMind string `ini:"-"` *Embeded `comment:"Embeded section"` } func main() { a := &Author{"Unknwon", true, 21, 2.8, "", &Embeded{ []time.Time{time.Now(), time.Now()}, []string{"HangZhou", "Boston"}, []int{}, }} cfg: = ini.Empty () err = ini.ReflectFrom (cfg, a) // ... }
NAME = Unknwon Male = true ; Author's age Age = 21 GPA = 2.8 ; Embeded section [Embeded] ; Time data Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 places = HangZhou,Boston
With ShadowLoad map
If you would like to cooperate ShadowLoad map a partition to the structure, you need to specify allowshadow
the label.
Suppose you have the following profile:
[IP] value = 192.168.31.201 value = 192.168.31.211 value = 192.168.31.221
You should define the structure corresponding to the following manner:
type IP struct { Value []string `ini:"value,omitempty,allowshadow"` }
If you do not need two before labeling rules can be used ini:",,allowshadow"
be abbreviated.
Mappings / Other information reflected
Any embedded default configuration will be recognized as a different partition, and does not automatically associate a so-called partition Sons:
type Child struct { Age string } type Parent struct { Name string Child } type Config struct { City string Parent }
Sample configuration file:
City = Boston [Parent] Name = Unknwon [Child] Age = 21
Good, but I just want to embed structure is also the same partition. Well, your father is Li Gang!
type Child struct { Age string } type Parent struct { Name string Child `ini:"Parent"` } type Config struct { City string Parent }
Sample configuration file:
City = Boston [Parent] Name = Unknwon Age = 21
Key Name Mapper (Name Mapper)
To save you time and simplify the code, this type of support for the library NameMapper
name mapper, mapping between the mapping field names with the structure responsible for the partition name and key name.
Currently it has 2 built-in mapper:
AllCapsUnderscore
: The mapper to convert the format field namesALL_CAPS_UNDERSCORE
and then to match the partition name and key name.TitleUnderscore
: The mapper to convert the format field namestitle_underscore
and then to match the partition name and key name.
Instructions:
type Info struct { PackageName string } func main() { err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini")) // ... cfg, err := ini.Load([]byte("PACKAGE_NAME=ini")) // ... info := new(Info) cfg.NameMapper = ini.AllCapsUnderscore err = cfg.MapTo(info) // ... }
Use functions ini.ReflectFromWithMapper
may also be applied the same rule.
Key mapper (Value Mapper)
Value mapper allows the use of a custom function values automatically expand specific content, such as access to the runtime environment variables:
type Env struct { Foo string `ini:"foo"` } func main() { cfg, err := ini.Load([]byte("[env]\nfoo = ${MY_VAR}\n")) cfg.ValueMapper = os.ExpandEnv // ... env := &Env{} err = cfg.Section("env").MapTo(env) }
In the present embodiment, env.Foo
it will be acquired by the environment variable runtime MY_VAR
values.