Usage:
- Init(rc_file1, rc_file2...) 后面的会覆盖前面的
- Get(section)
在配置文件中可以使用COMMENT_FLAG
默认 #
来写注解.
package config
import (
"fmt"
"io/ioutil"
"log"
"os"
"regexp"
"strings"
)
var CONFIG map[string]map[string]string = make(map[string]map[string]string)
const COMMENT_FLAG = "#"
var RE_SESSION *regexp.Regexp = regexp.MustCompile("^\\[.+\\]$")
var RE_KV *regexp.Regexp = regexp.MustCompile("^.+\\=.+$")
func exists(file string) bool {
if f, err := os.Open(file); err != nil && os.IsNotExist(err) {
defer f.Close()
return false
}
return true
}
func parse_line(line string) (string, interface{}) {
comment_index := strings.Index(line, COMMENT_FLAG)
if comment_index != -1 {
line = line[0:comment_index]
}
if RE_SESSION.MatchString(line) {
return "section", line[1 : len(line)-1]
}
if RE_KV.MatchString(line) {
parts := strings.SplitN(line, "=", 2)
return "kv", map[string]string{strings.TrimSpace(parts[0]): strings.TrimSpace(parts[1])}
}
fmt.Fprintf(os.Stderr, "%s format error!\n", line)
return "other", nil
}
func Get(section string) (map[string]string, error) {
config, ok := CONFIG[section]
if ok {
return config, nil
}
return nil, fmt.Errorf("section %s not found", section)
}
func Sections() (sections []string) {
for section, _ := range CONFIG {
sections = append(sections, section)
}
return sections
}
func parse_rc_content(content string) map[string]map[string]string {
config := make(map[string]map[string]string)
lines := strings.FieldsFunc(content, func(char rune) bool {
return strings.ContainsRune("\r\n", char)
})
last_section := ""
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
line_type, line_value := parse_line(line)
switch {
case line_type == "section":
section := line_value.(string)
if _, ok := config[section]; ok == false {
config[section] = make(map[string]string)
}
last_section = section
case line_type == "kv" && last_section != "":
kv := line_value.(map[string]string)
for k, v := range kv {
config[last_section][k] = v
}
}
}
return config
}
func Init(rc_files ...string) {
for _, rc_file := range rc_files {
if !exists(rc_file) {
continue
}
content_bytes, err := ioutil.ReadFile(rc_file)
if err != nil {
log.Fatalln(err)
}
content := string(content_bytes)
for section, configs := range parse_rc_content(content) {
_, ok := CONFIG[section]
if ok == false {
CONFIG[section] = make(map[string]string)
}
for name, value := range configs {
CONFIG[section][name] = value
}
}
}
}