diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/go-ini/ini/README_ZH.md | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/go-ini/ini/README_ZH.md')
-rw-r--r-- | vendor/github.com/go-ini/ini/README_ZH.md | 727 |
1 files changed, 0 insertions, 727 deletions
diff --git a/vendor/github.com/go-ini/ini/README_ZH.md b/vendor/github.com/go-ini/ini/README_ZH.md deleted file mode 100644 index 163432d..0000000 --- a/vendor/github.com/go-ini/ini/README_ZH.md +++ /dev/null | |||
@@ -1,727 +0,0 @@ | |||
1 | 本包提供了 Go 语言中读写 INI 文件的功能。 | ||
2 | |||
3 | ## 功能特性 | ||
4 | |||
5 | - 支持覆盖加载多个数据源(`[]byte`、文件和 `io.ReadCloser`) | ||
6 | - 支持递归读取键值 | ||
7 | - 支持读取父子分区 | ||
8 | - 支持读取自增键名 | ||
9 | - 支持读取多行的键值 | ||
10 | - 支持大量辅助方法 | ||
11 | - 支持在读取时直接转换为 Go 语言类型 | ||
12 | - 支持读取和 **写入** 分区和键的注释 | ||
13 | - 轻松操作分区、键值和注释 | ||
14 | - 在保存文件时分区和键值会保持原有的顺序 | ||
15 | |||
16 | ## 下载安装 | ||
17 | |||
18 | 使用一个特定版本: | ||
19 | |||
20 | go get gopkg.in/ini.v1 | ||
21 | |||
22 | 使用最新版: | ||
23 | |||
24 | go get github.com/go-ini/ini | ||
25 | |||
26 | 如需更新请添加 `-u` 选项。 | ||
27 | |||
28 | ### 测试安装 | ||
29 | |||
30 | 如果您想要在自己的机器上运行测试,请使用 `-t` 标记: | ||
31 | |||
32 | go get -t gopkg.in/ini.v1 | ||
33 | |||
34 | 如需更新请添加 `-u` 选项。 | ||
35 | |||
36 | ## 开始使用 | ||
37 | |||
38 | ### 从数据源加载 | ||
39 | |||
40 | 一个 **数据源** 可以是 `[]byte` 类型的原始数据,`string` 类型的文件路径或 `io.ReadCloser`。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。 | ||
41 | |||
42 | ```go | ||
43 | cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data")))) | ||
44 | ``` | ||
45 | |||
46 | 或者从一个空白的文件开始: | ||
47 | |||
48 | ```go | ||
49 | cfg := ini.Empty() | ||
50 | ``` | ||
51 | |||
52 | 当您在一开始无法决定需要加载哪些数据源时,仍可以使用 **Append()** 在需要的时候加载它们。 | ||
53 | |||
54 | ```go | ||
55 | err := cfg.Append("other file", []byte("other raw data")) | ||
56 | ``` | ||
57 | |||
58 | 当您想要加载一系列文件,但是不能够确定其中哪些文件是不存在的,可以通过调用函数 `LooseLoad` 来忽略它们(`Load` 会因为文件不存在而返回错误): | ||
59 | |||
60 | ```go | ||
61 | cfg, err := ini.LooseLoad("filename", "filename_404") | ||
62 | ``` | ||
63 | |||
64 | 更牛逼的是,当那些之前不存在的文件在重新调用 `Reload` 方法的时候突然出现了,那么它们会被正常加载。 | ||
65 | |||
66 | #### 忽略键名的大小写 | ||
67 | |||
68 | 有时候分区和键的名称大小写混合非常烦人,这个时候就可以通过 `InsensitiveLoad` 将所有分区和键名在读取里强制转换为小写: | ||
69 | |||
70 | ```go | ||
71 | cfg, err := ini.InsensitiveLoad("filename") | ||
72 | //... | ||
73 | |||
74 | // sec1 和 sec2 指向同一个分区对象 | ||
75 | sec1, err := cfg.GetSection("Section") | ||
76 | sec2, err := cfg.GetSection("SecTIOn") | ||
77 | |||
78 | // key1 和 key2 指向同一个键对象 | ||
79 | key1, err := cfg.GetKey("Key") | ||
80 | key2, err := cfg.GetKey("KeY") | ||
81 | ``` | ||
82 | |||
83 | #### 类似 MySQL 配置中的布尔值键 | ||
84 | |||
85 | MySQL 的配置文件中会出现没有具体值的布尔类型的键: | ||
86 | |||
87 | ```ini | ||
88 | [mysqld] | ||
89 | ... | ||
90 | skip-host-cache | ||
91 | skip-name-resolve | ||
92 | ``` | ||
93 | |||
94 | 默认情况下这被认为是缺失值而无法完成解析,但可以通过高级的加载选项对它们进行处理: | ||
95 | |||
96 | ```go | ||
97 | cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf")) | ||
98 | ``` | ||
99 | |||
100 | 这些键的值永远为 `true`,且在保存到文件时也只会输出键名。 | ||
101 | |||
102 | 如果您想要通过程序来生成此类键,则可以使用 `NewBooleanKey`: | ||
103 | |||
104 | ```go | ||
105 | key, err := sec.NewBooleanKey("skip-host-cache") | ||
106 | ``` | ||
107 | |||
108 | #### 关于注释 | ||
109 | |||
110 | 下述几种情况的内容将被视为注释: | ||
111 | |||
112 | 1. 所有以 `#` 或 `;` 开头的行 | ||
113 | 2. 所有在 `#` 或 `;` 之后的内容 | ||
114 | 3. 分区标签后的文字 (即 `[分区名]` 之后的内容) | ||
115 | |||
116 | 如果你希望使用包含 `#` 或 `;` 的值,请使用 ``` ` ``` 或 ``` """ ``` 进行包覆。 | ||
117 | |||
118 | ### 操作分区(Section) | ||
119 | |||
120 | 获取指定分区: | ||
121 | |||
122 | ```go | ||
123 | section, err := cfg.GetSection("section name") | ||
124 | ``` | ||
125 | |||
126 | 如果您想要获取默认分区,则可以用空字符串代替分区名: | ||
127 | |||
128 | ```go | ||
129 | section, err := cfg.GetSection("") | ||
130 | ``` | ||
131 | |||
132 | 当您非常确定某个分区是存在的,可以使用以下简便方法: | ||
133 | |||
134 | ```go | ||
135 | section := cfg.Section("section name") | ||
136 | ``` | ||
137 | |||
138 | 如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会自动创建并返回一个对应的分区对象给您。 | ||
139 | |||
140 | 创建一个分区: | ||
141 | |||
142 | ```go | ||
143 | err := cfg.NewSection("new section") | ||
144 | ``` | ||
145 | |||
146 | 获取所有分区对象或名称: | ||
147 | |||
148 | ```go | ||
149 | sections := cfg.Sections() | ||
150 | names := cfg.SectionStrings() | ||
151 | ``` | ||
152 | |||
153 | ### 操作键(Key) | ||
154 | |||
155 | 获取某个分区下的键: | ||
156 | |||
157 | ```go | ||
158 | key, err := cfg.Section("").GetKey("key name") | ||
159 | ``` | ||
160 | |||
161 | 和分区一样,您也可以直接获取键而忽略错误处理: | ||
162 | |||
163 | ```go | ||
164 | key := cfg.Section("").Key("key name") | ||
165 | ``` | ||
166 | |||
167 | 判断某个键是否存在: | ||
168 | |||
169 | ```go | ||
170 | yes := cfg.Section("").HasKey("key name") | ||
171 | ``` | ||
172 | |||
173 | 创建一个新的键: | ||
174 | |||
175 | ```go | ||
176 | err := cfg.Section("").NewKey("name", "value") | ||
177 | ``` | ||
178 | |||
179 | 获取分区下的所有键或键名: | ||
180 | |||
181 | ```go | ||
182 | keys := cfg.Section("").Keys() | ||
183 | names := cfg.Section("").KeyStrings() | ||
184 | ``` | ||
185 | |||
186 | 获取分区下的所有键值对的克隆: | ||
187 | |||
188 | ```go | ||
189 | hash := cfg.Section("").KeysHash() | ||
190 | ``` | ||
191 | |||
192 | ### 操作键值(Value) | ||
193 | |||
194 | 获取一个类型为字符串(string)的值: | ||
195 | |||
196 | ```go | ||
197 | val := cfg.Section("").Key("key name").String() | ||
198 | ``` | ||
199 | |||
200 | 获取值的同时通过自定义函数进行处理验证: | ||
201 | |||
202 | ```go | ||
203 | val := cfg.Section("").Key("key name").Validate(func(in string) string { | ||
204 | if len(in) == 0 { | ||
205 | return "default" | ||
206 | } | ||
207 | return in | ||
208 | }) | ||
209 | ``` | ||
210 | |||
211 | 如果您不需要任何对值的自动转变功能(例如递归读取),可以直接获取原值(这种方式性能最佳): | ||
212 | |||
213 | ```go | ||
214 | val := cfg.Section("").Key("key name").Value() | ||
215 | ``` | ||
216 | |||
217 | 判断某个原值是否存在: | ||
218 | |||
219 | ```go | ||
220 | yes := cfg.Section("").HasValue("test value") | ||
221 | ``` | ||
222 | |||
223 | 获取其它类型的值: | ||
224 | |||
225 | ```go | ||
226 | // 布尔值的规则: | ||
227 | // true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On | ||
228 | // false 当值为:0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off | ||
229 | v, err = cfg.Section("").Key("BOOL").Bool() | ||
230 | v, err = cfg.Section("").Key("FLOAT64").Float64() | ||
231 | v, err = cfg.Section("").Key("INT").Int() | ||
232 | v, err = cfg.Section("").Key("INT64").Int64() | ||
233 | v, err = cfg.Section("").Key("UINT").Uint() | ||
234 | v, err = cfg.Section("").Key("UINT64").Uint64() | ||
235 | v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339) | ||
236 | v, err = cfg.Section("").Key("TIME").Time() // RFC3339 | ||
237 | |||
238 | v = cfg.Section("").Key("BOOL").MustBool() | ||
239 | v = cfg.Section("").Key("FLOAT64").MustFloat64() | ||
240 | v = cfg.Section("").Key("INT").MustInt() | ||
241 | v = cfg.Section("").Key("INT64").MustInt64() | ||
242 | v = cfg.Section("").Key("UINT").MustUint() | ||
243 | v = cfg.Section("").Key("UINT64").MustUint64() | ||
244 | v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339) | ||
245 | v = cfg.Section("").Key("TIME").MustTime() // RFC3339 | ||
246 | |||
247 | // 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值, | ||
248 | // 当键不存在或者转换失败时,则会直接返回该默认值。 | ||
249 | // 但是,MustString 方法必须传递一个默认值。 | ||
250 | |||
251 | v = cfg.Seciont("").Key("String").MustString("default") | ||
252 | v = cfg.Section("").Key("BOOL").MustBool(true) | ||
253 | v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25) | ||
254 | v = cfg.Section("").Key("INT").MustInt(10) | ||
255 | v = cfg.Section("").Key("INT64").MustInt64(99) | ||
256 | v = cfg.Section("").Key("UINT").MustUint(3) | ||
257 | v = cfg.Section("").Key("UINT64").MustUint64(6) | ||
258 | v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now()) | ||
259 | v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339 | ||
260 | ``` | ||
261 | |||
262 | 如果我的值有好多行怎么办? | ||
263 | |||
264 | ```ini | ||
265 | [advance] | ||
266 | ADDRESS = """404 road, | ||
267 | NotFound, State, 5000 | ||
268 | Earth""" | ||
269 | ``` | ||
270 | |||
271 | 嗯哼?小 case! | ||
272 | |||
273 | ```go | ||
274 | cfg.Section("advance").Key("ADDRESS").String() | ||
275 | |||
276 | /* --- start --- | ||
277 | 404 road, | ||
278 | NotFound, State, 5000 | ||
279 | Earth | ||
280 | ------ end --- */ | ||
281 | ``` | ||
282 | |||
283 | 赞爆了!那要是我属于一行的内容写不下想要写到第二行怎么办? | ||
284 | |||
285 | ```ini | ||
286 | [advance] | ||
287 | two_lines = how about \ | ||
288 | continuation lines? | ||
289 | lots_of_lines = 1 \ | ||
290 | 2 \ | ||
291 | 3 \ | ||
292 | 4 | ||
293 | ``` | ||
294 | |||
295 | 简直是小菜一碟! | ||
296 | |||
297 | ```go | ||
298 | cfg.Section("advance").Key("two_lines").String() // how about continuation lines? | ||
299 | cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4 | ||
300 | ``` | ||
301 | |||
302 | 可是我有时候觉得两行连在一起特别没劲,怎么才能不自动连接两行呢? | ||
303 | |||
304 | ```go | ||
305 | cfg, err := ini.LoadSources(ini.LoadOptions{ | ||
306 | IgnoreContinuation: true, | ||
307 | }, "filename") | ||
308 | ``` | ||
309 | |||
310 | 哇靠给力啊! | ||
311 | |||
312 | 需要注意的是,值两侧的单引号会被自动剔除: | ||
313 | |||
314 | ```ini | ||
315 | foo = "some value" // foo: some value | ||
316 | bar = 'some value' // bar: some value | ||
317 | ``` | ||
318 | |||
319 | 这就是全部了?哈哈,当然不是。 | ||
320 | |||
321 | #### 操作键值的辅助方法 | ||
322 | |||
323 | 获取键值时设定候选值: | ||
324 | |||
325 | ```go | ||
326 | v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"}) | ||
327 | v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75}) | ||
328 | v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30}) | ||
329 | v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30}) | ||
330 | v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9}) | ||
331 | v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9}) | ||
332 | v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3}) | ||
333 | v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339 | ||
334 | ``` | ||
335 | |||
336 | 如果获取到的值不是候选值的任意一个,则会返回默认值,而默认值不需要是候选值中的一员。 | ||
337 | |||
338 | 验证获取的值是否在指定范围内: | ||
339 | |||
340 | ```go | ||
341 | vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2) | ||
342 | vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20) | ||
343 | vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20) | ||
344 | vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9) | ||
345 | vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9) | ||
346 | vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime) | ||
347 | vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339 | ||
348 | ``` | ||
349 | |||
350 | ##### 自动分割键值到切片(slice) | ||
351 | |||
352 | 当存在无效输入时,使用零值代替: | ||
353 | |||
354 | ```go | ||
355 | // Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] | ||
356 | // Input: how, 2.2, are, you -> [0.0 2.2 0.0 0.0] | ||
357 | vals = cfg.Section("").Key("STRINGS").Strings(",") | ||
358 | vals = cfg.Section("").Key("FLOAT64S").Float64s(",") | ||
359 | vals = cfg.Section("").Key("INTS").Ints(",") | ||
360 | vals = cfg.Section("").Key("INT64S").Int64s(",") | ||
361 | vals = cfg.Section("").Key("UINTS").Uints(",") | ||
362 | vals = cfg.Section("").Key("UINT64S").Uint64s(",") | ||
363 | vals = cfg.Section("").Key("TIMES").Times(",") | ||
364 | ``` | ||
365 | |||
366 | 从结果切片中剔除无效输入: | ||
367 | |||
368 | ```go | ||
369 | // Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] | ||
370 | // Input: how, 2.2, are, you -> [2.2] | ||
371 | vals = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",") | ||
372 | vals = cfg.Section("").Key("INTS").ValidInts(",") | ||
373 | vals = cfg.Section("").Key("INT64S").ValidInt64s(",") | ||
374 | vals = cfg.Section("").Key("UINTS").ValidUints(",") | ||
375 | vals = cfg.Section("").Key("UINT64S").ValidUint64s(",") | ||
376 | vals = cfg.Section("").Key("TIMES").ValidTimes(",") | ||
377 | ``` | ||
378 | |||
379 | 当存在无效输入时,直接返回错误: | ||
380 | |||
381 | ```go | ||
382 | // Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] | ||
383 | // Input: how, 2.2, are, you -> error | ||
384 | vals = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",") | ||
385 | vals = cfg.Section("").Key("INTS").StrictInts(",") | ||
386 | vals = cfg.Section("").Key("INT64S").StrictInt64s(",") | ||
387 | vals = cfg.Section("").Key("UINTS").StrictUints(",") | ||
388 | vals = cfg.Section("").Key("UINT64S").StrictUint64s(",") | ||
389 | vals = cfg.Section("").Key("TIMES").StrictTimes(",") | ||
390 | ``` | ||
391 | |||
392 | ### 保存配置 | ||
393 | |||
394 | 终于到了这个时刻,是时候保存一下配置了。 | ||
395 | |||
396 | 比较原始的做法是输出配置到某个文件: | ||
397 | |||
398 | ```go | ||
399 | // ... | ||
400 | err = cfg.SaveTo("my.ini") | ||
401 | err = cfg.SaveToIndent("my.ini", "\t") | ||
402 | ``` | ||
403 | |||
404 | 另一个比较高级的做法是写入到任何实现 `io.Writer` 接口的对象中: | ||
405 | |||
406 | ```go | ||
407 | // ... | ||
408 | cfg.WriteTo(writer) | ||
409 | cfg.WriteToIndent(writer, "\t") | ||
410 | ``` | ||
411 | |||
412 | 默认情况下,空格将被用于对齐键值之间的等号以美化输出结果,以下代码可以禁用该功能: | ||
413 | |||
414 | ```go | ||
415 | ini.PrettyFormat = false | ||
416 | ``` | ||
417 | |||
418 | ## 高级用法 | ||
419 | |||
420 | ### 递归读取键值 | ||
421 | |||
422 | 在获取所有键值的过程中,特殊语法 `%(<name>)s` 会被应用,其中 `<name>` 可以是相同分区或者默认分区下的键名。字符串 `%(<name>)s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。 | ||
423 | |||
424 | ```ini | ||
425 | NAME = ini | ||
426 | |||
427 | [author] | ||
428 | NAME = Unknwon | ||
429 | GITHUB = https://github.com/%(NAME)s | ||
430 | |||
431 | [package] | ||
432 | FULL_NAME = github.com/go-ini/%(NAME)s | ||
433 | ``` | ||
434 | |||
435 | ```go | ||
436 | cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon | ||
437 | cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini | ||
438 | ``` | ||
439 | |||
440 | ### 读取父子分区 | ||
441 | |||
442 | 您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。 | ||
443 | |||
444 | ```ini | ||
445 | NAME = ini | ||
446 | VERSION = v1 | ||
447 | IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s | ||
448 | |||
449 | [package] | ||
450 | CLONE_URL = https://%(IMPORT_PATH)s | ||
451 | |||
452 | [package.sub] | ||
453 | ``` | ||
454 | |||
455 | ```go | ||
456 | cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1 | ||
457 | ``` | ||
458 | |||
459 | #### 获取上级父分区下的所有键名 | ||
460 | |||
461 | ```go | ||
462 | cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"] | ||
463 | ``` | ||
464 | |||
465 | ### 无法解析的分区 | ||
466 | |||
467 | 如果遇到一些比较特殊的分区,它们不包含常见的键值对,而是没有固定格式的纯文本,则可以使用 `LoadOptions.UnparsableSections` 进行处理: | ||
468 | |||
469 | ```go | ||
470 | cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] | ||
471 | <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1>`)) | ||
472 | |||
473 | body := cfg.Section("COMMENTS").Body() | ||
474 | |||
475 | /* --- start --- | ||
476 | <1><L.Slide#2> This slide has the fuel listed in the wrong units <e.1> | ||
477 | ------ end --- */ | ||
478 | ``` | ||
479 | |||
480 | ### 读取自增键名 | ||
481 | |||
482 | 如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。 | ||
483 | |||
484 | ```ini | ||
485 | [features] | ||
486 | -: Support read/write comments of keys and sections | ||
487 | -: Support auto-increment of key names | ||
488 | -: Support load multiple files to overwrite key values | ||
489 | ``` | ||
490 | |||
491 | ```go | ||
492 | cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"} | ||
493 | ``` | ||
494 | |||
495 | ### 映射到结构 | ||
496 | |||
497 | 想要使用更加面向对象的方式玩转 INI 吗?好主意。 | ||
498 | |||
499 | ```ini | ||
500 | Name = Unknwon | ||
501 | age = 21 | ||
502 | Male = true | ||
503 | Born = 1993-01-01T20:17:05Z | ||
504 | |||
505 | [Note] | ||
506 | Content = Hi is a good man! | ||
507 | Cities = HangZhou, Boston | ||
508 | ``` | ||
509 | |||
510 | ```go | ||
511 | type Note struct { | ||
512 | Content string | ||
513 | Cities []string | ||
514 | } | ||
515 | |||
516 | type Person struct { | ||
517 | Name string | ||
518 | Age int `ini:"age"` | ||
519 | Male bool | ||
520 | Born time.Time | ||
521 | Note | ||
522 | Created time.Time `ini:"-"` | ||
523 | } | ||
524 | |||
525 | func main() { | ||
526 | cfg, err := ini.Load("path/to/ini") | ||
527 | // ... | ||
528 | p := new(Person) | ||
529 | err = cfg.MapTo(p) | ||
530 | // ... | ||
531 | |||
532 | // 一切竟可以如此的简单。 | ||
533 | err = ini.MapTo(p, "path/to/ini") | ||
534 | // ... | ||
535 | |||
536 | // 嗯哼?只需要映射一个分区吗? | ||
537 | n := new(Note) | ||
538 | err = cfg.Section("Note").MapTo(n) | ||
539 | // ... | ||
540 | } | ||
541 | ``` | ||
542 | |||
543 | 结构的字段怎么设置默认值呢?很简单,只要在映射之前对指定字段进行赋值就可以了。如果键未找到或者类型错误,该值不会发生改变。 | ||
544 | |||
545 | ```go | ||
546 | // ... | ||
547 | p := &Person{ | ||
548 | Name: "Joe", | ||
549 | } | ||
550 | // ... | ||
551 | ``` | ||
552 | |||
553 | 这样玩 INI 真的好酷啊!然而,如果不能还给我原来的配置文件,有什么卵用? | ||
554 | |||
555 | ### 从结构反射 | ||
556 | |||
557 | 可是,我有说不能吗? | ||
558 | |||
559 | ```go | ||
560 | type Embeded struct { | ||
561 | Dates []time.Time `delim:"|"` | ||
562 | Places []string `ini:"places,omitempty"` | ||
563 | None []int `ini:",omitempty"` | ||
564 | } | ||
565 | |||
566 | type Author struct { | ||
567 | Name string `ini:"NAME"` | ||
568 | Male bool | ||
569 | Age int | ||
570 | GPA float64 | ||
571 | NeverMind string `ini:"-"` | ||
572 | *Embeded | ||
573 | } | ||
574 | |||
575 | func main() { | ||
576 | a := &Author{"Unknwon", true, 21, 2.8, "", | ||
577 | &Embeded{ | ||
578 | []time.Time{time.Now(), time.Now()}, | ||
579 | []string{"HangZhou", "Boston"}, | ||
580 | []int{}, | ||
581 | }} | ||
582 | cfg := ini.Empty() | ||
583 | err = ini.ReflectFrom(cfg, a) | ||
584 | // ... | ||
585 | } | ||
586 | ``` | ||
587 | |||
588 | 瞧瞧,奇迹发生了。 | ||
589 | |||
590 | ```ini | ||
591 | NAME = Unknwon | ||
592 | Male = true | ||
593 | Age = 21 | ||
594 | GPA = 2.8 | ||
595 | |||
596 | [Embeded] | ||
597 | Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 | ||
598 | places = HangZhou,Boston | ||
599 | ``` | ||
600 | |||
601 | #### 名称映射器(Name Mapper) | ||
602 | |||
603 | 为了节省您的时间并简化代码,本库支持类型为 [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) 的名称映射器,该映射器负责结构字段名与分区名和键名之间的映射。 | ||
604 | |||
605 | 目前有 2 款内置的映射器: | ||
606 | |||
607 | - `AllCapsUnderscore`:该映射器将字段名转换至格式 `ALL_CAPS_UNDERSCORE` 后再去匹配分区名和键名。 | ||
608 | - `TitleUnderscore`:该映射器将字段名转换至格式 `title_underscore` 后再去匹配分区名和键名。 | ||
609 | |||
610 | 使用方法: | ||
611 | |||
612 | ```go | ||
613 | type Info struct{ | ||
614 | PackageName string | ||
615 | } | ||
616 | |||
617 | func main() { | ||
618 | err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini")) | ||
619 | // ... | ||
620 | |||
621 | cfg, err := ini.Load([]byte("PACKAGE_NAME=ini")) | ||
622 | // ... | ||
623 | info := new(Info) | ||
624 | cfg.NameMapper = ini.AllCapsUnderscore | ||
625 | err = cfg.MapTo(info) | ||
626 | // ... | ||
627 | } | ||
628 | ``` | ||
629 | |||
630 | 使用函数 `ini.ReflectFromWithMapper` 时也可应用相同的规则。 | ||
631 | |||
632 | #### 值映射器(Value Mapper) | ||
633 | |||
634 | 值映射器允许使用一个自定义函数自动展开值的具体内容,例如:运行时获取环境变量: | ||
635 | |||
636 | ```go | ||
637 | type Env struct { | ||
638 | Foo string `ini:"foo"` | ||
639 | } | ||
640 | |||
641 | func main() { | ||
642 | cfg, err := ini.Load([]byte("[env]\nfoo = ${MY_VAR}\n") | ||
643 | cfg.ValueMapper = os.ExpandEnv | ||
644 | // ... | ||
645 | env := &Env{} | ||
646 | err = cfg.Section("env").MapTo(env) | ||
647 | } | ||
648 | ``` | ||
649 | |||
650 | 本例中,`env.Foo` 将会是运行时所获取到环境变量 `MY_VAR` 的值。 | ||
651 | |||
652 | #### 映射/反射的其它说明 | ||
653 | |||
654 | 任何嵌入的结构都会被默认认作一个不同的分区,并且不会自动产生所谓的父子分区关联: | ||
655 | |||
656 | ```go | ||
657 | type Child struct { | ||
658 | Age string | ||
659 | } | ||
660 | |||
661 | type Parent struct { | ||
662 | Name string | ||
663 | Child | ||
664 | } | ||
665 | |||
666 | type Config struct { | ||
667 | City string | ||
668 | Parent | ||
669 | } | ||
670 | ``` | ||
671 | |||
672 | 示例配置文件: | ||
673 | |||
674 | ```ini | ||
675 | City = Boston | ||
676 | |||
677 | [Parent] | ||
678 | Name = Unknwon | ||
679 | |||
680 | [Child] | ||
681 | Age = 21 | ||
682 | ``` | ||
683 | |||
684 | 很好,但是,我就是要嵌入结构也在同一个分区。好吧,你爹是李刚! | ||
685 | |||
686 | ```go | ||
687 | type Child struct { | ||
688 | Age string | ||
689 | } | ||
690 | |||
691 | type Parent struct { | ||
692 | Name string | ||
693 | Child `ini:"Parent"` | ||
694 | } | ||
695 | |||
696 | type Config struct { | ||
697 | City string | ||
698 | Parent | ||
699 | } | ||
700 | ``` | ||
701 | |||
702 | 示例配置文件: | ||
703 | |||
704 | ```ini | ||
705 | City = Boston | ||
706 | |||
707 | [Parent] | ||
708 | Name = Unknwon | ||
709 | Age = 21 | ||
710 | ``` | ||
711 | |||
712 | ## 获取帮助 | ||
713 | |||
714 | - [API 文档](https://gowalker.org/gopkg.in/ini.v1) | ||
715 | - [创建工单](https://github.com/go-ini/ini/issues/new) | ||
716 | |||
717 | ## 常见问题 | ||
718 | |||
719 | ### 字段 `BlockMode` 是什么? | ||
720 | |||
721 | 默认情况下,本库会在您进行读写操作时采用锁机制来确保数据时间。但在某些情况下,您非常确定只进行读操作。此时,您可以通过设置 `cfg.BlockMode = false` 来将读操作提升大约 **50-70%** 的性能。 | ||
722 | |||
723 | ### 为什么要写另一个 INI 解析库? | ||
724 | |||
725 | 许多人都在使用我的 [goconfig](https://github.com/Unknwon/goconfig) 来完成对 INI 文件的操作,但我希望使用更加 Go 风格的代码。并且当您设置 `cfg.BlockMode = false` 时,会有大约 **10-30%** 的性能提升。 | ||
726 | |||
727 | 为了做出这些改变,我必须对 API 进行破坏,所以新开一个仓库是最安全的做法。除此之外,本库直接使用 `gopkg.in` 来进行版本化发布。(其实真相是导入路径更短了) | ||