iniutils for Golang
一直有计划将 Delphi 中的譬如 TIniFile 等相关功能移植到 Golang,这些设施在 Delphi 中(相对而言)比较常用,使用起来也非常方便。
虽然 Github 上早已有这些三方库,但我还是想仿照 Delphi 的做法来实现一套(其实只是模仿了 TMemIniFile 而非 Windows 下的 TIniFile 实现,暂时无意愿去实现 GetPrivateProfileXXX/WritePrivateProfileXXX 等 API),并额外提供直接干脆的调用接口等。
代码已托管至 Github。
// Copyright 2017 ecofast(无尽愿). All rights reserved.
// Use of this source code is governed by a BSD-style license.
// Package iniutils was translated from TMemIniFile in Delphi(2007) RTL,
// which loads an entire INI file into memory
// and allows all operations to be performed on the memory image.
// The image can then be written out to the disk file.
package iniutils
import (
"bufio"
"bytes"
"fmt"
"os"
"strconv"
"strings"
"github.com/ecofast/sysutils"
)
type IniFile struct {
fileName string
caseSensitive bool
sections map[string][]string
}
func NewIniFile(filename string, casesensitive bool) *IniFile {
ini := &IniFile{
fileName: filename,
caseSensitive: casesensitive,
sections: make(map[string][]string),
}
ini.loadValues()
return ini
}
func (ini *IniFile) FileName() string {
return ini.fileName
}
func (ini *IniFile) CaseSensitive() bool {
return ini.caseSensitive
}
func (ini *IniFile) String() string {
var buf bytes.Buffer
for sec, lst := range ini.sections {
buf.WriteString(fmt.Sprintf("[%s]\n", sec))
for _, s := range lst {
buf.WriteString(fmt.Sprintf("%s\n", s))
}
buf.WriteString("\n")
}
return buf.String()
}
func (ini *IniFile) getRealValue(s string) string {
if !ini.caseSensitive {
return strings.ToLower(s)
}
return s
}
func (ini *IniFile) loadValues() {
if !sysutils.FileExists(ini.fileName) {
return
}
file, err := os.Open(ini.fileName)
if err != nil {
return
}
defer file.Close()
section := ""
scanner := bufio.NewScanner(file)
for scanner.Scan() {
s := scanner.Text()
s = strings.TrimSpace(s)
s = ini.getRealValue(s)
if s != "" && s[0] != ';' {
if s[0] == '[' && s[len(s)-1] == ']' {
s = s[1 : len(s)-1]
section = s
} else {
if section != "" {
if pos := strings.Index(s, "="); pos > 0 {
if sl, ok := ini.sections[section]; ok {
ini.sections[section] = append(sl, s)
} else {
ini.sections[section] = []string{s}
}
} else {
// ingore invalid ident
//
}
}
}
}
}
}
func (ini *IniFile) flush() {
file, err := os.Create(ini.fileName)
sysutils.CheckError(err)
defer file.Close()
fw := bufio.NewWriter(file)
for sec, lst := range ini.sections {
_, err = fw.WriteString(fmt.Sprintf("[%s]\n", sec))
sysutils.CheckError(err)
for _, s := range lst {
_, err = fw.WriteString(fmt.Sprintf("%s\n", s))
sysutils.CheckError(err)
}
_, err = fw.WriteString("\n")
sysutils.CheckError(err)
}
fw.Flush()
}
func (ini *IniFile) SectionExists(section string) bool {
sec := ini.getRealValue(section)
if _, ok := ini.sections[sec]; ok {
return true
}
return false
}
func (ini *IniFile) ReadSections() []string {
var ss []string
for sec, _ := range ini.sections {
ss = append(ss, sec)
}
return ss
}
func (ini *IniFile) EraseSection(section string) {
sec := ini.getRealValue(section)
delete(ini.sections, sec)
}
func (ini *IniFile) ReadSectionIdents(section string) []string {
var ss []string
sec := ini.getRealValue(section)
if sl, ok := ini.sections[sec]; ok {
for _, s := range sl {
if pos := strings.Index(s, "="); pos > 0 {
ss = append(ss, s[0:pos])
}
}
}
return ss
}
func (ini *IniFile) ReadSectionValues(section string) []string {
var ss []string
sec := ini.getRealValue(section)
if sl, ok := ini.sections[sec]; ok {
for _, s := range sl {
ss = append(ss, s)
}
}
return ss
}
func (ini *IniFile) DeleteIdent(section, ident string) {
sec := ini.getRealValue(section)
id := ini.getRealValue(ident)
if sl, ok := ini.sections[sec]; ok {
for i := 0; i < len(sl); i++ {
s := sl[i]
if pos := strings.Index(s, "="); pos > 0 {
if s[0:pos] == id {
var ss []string
for j := 0; j < i; j++ {
ss = append(ss, sl[j])
}
for j := i + 1; j < len(sl); j++ {
ss = append(ss, sl[j])
}
ini.sections[sec] = ss
return
}
}
}
}
}
func (ini *IniFile) IdentExists(section, ident string) bool {
sec := ini.getRealValue(section)
id := ini.getRealValue(ident)
if sl, ok := ini.sections[sec]; ok {
for _, s := range sl {
if pos := strings.Index(s, "="); pos > 0 {
if s[0:pos] == id {
return true
}
}
}
}
return false
}
func (ini *IniFile) ReadString(section, ident, defaultValue string) string {
sec := ini.getRealValue(section)
id := ini.getRealValue(ident)
if sl, ok := ini.sections[sec]; ok {
for _, s := range sl {
if pos := strings.Index(s, "="); pos > 0 {
if s[0:pos] == id {
return s[pos+1:]
}
}
}
}
return defaultValue
}
func (ini *IniFile) WriteString(section, ident, value string) {
sec := ini.getRealValue(section)
id := ini.getRealValue(ident)
if sl, ok := ini.sections[sec]; ok {
for i := 0; i < len(sl); i++ {
s := sl[i]
if pos := strings.Index(s, "="); pos > 0 {
if s[0:pos] == id {
var ss []string
for j := 0; j < i; j++ {
ss = append(ss, sl[j])
}
ss = append(ss, ident+"="+value)
for j := i + 1; j < len(sl); j++ {
ss = append(ss, sl[j])
}
ini.sections[sec] = ss
return
}
}
}
ini.sections[sec] = append(sl, ident+"="+value)
} else {
ini.sections[sec] = []string{ident + "=" + value}
}
}
func (ini *IniFile) ReadInt(section, ident string, defaultValue int) int {
s := ini.ReadString(section, ident, "")
if ret, err := strconv.Atoi(s); err == nil {
return ret
} else {
return defaultValue
}
}
func (ini *IniFile) WriteInt(section, ident string, value int) {
ini.WriteString(section, ident, strconv.Itoa(value))
}
func (ini *IniFile) ReadBool(section, ident string, defaultValue bool) bool {
s := ini.ReadString(section, ident, sysutils.BoolToStr(defaultValue))
return sysutils.StrToBool(s)
}
func (ini *IniFile) WriteBool(section, ident string, value bool) {
ini.WriteString(section, ident, sysutils.BoolToStr(value))
}
func (ini *IniFile) ReadFloat(section, ident string, defaultValue float64) float64 {
s := ini.ReadString(section, ident, "")
if s != "" {
if ret, err := strconv.ParseFloat(s, 64); err == nil {
return ret
}
}
return defaultValue
}
func (ini *IniFile) WriteFloat(section, ident string, value float64) {
ini.WriteString(section, ident, sysutils.FloatToStr(value))
}
func (ini *IniFile) Close() {
ini.flush()
}
func (ini *IniFile) Clear() {
ini.sections = make(map[string][]string)
}
func IniReadString(fileName, section, ident, defaultValue string) string {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
return inifile.ReadString(section, ident, defaultValue)
}
func IniWriteString(fileName, section, ident, value string) {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
inifile.WriteString(section, ident, value)
}
func IniReadInt(fileName, section, ident string, defaultValue int) int {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
return inifile.ReadInt(section, ident, defaultValue)
}
func IniWriteInt(fileName, section, ident string, value int) {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
inifile.WriteInt(section, ident, value)
}
func IniSectionExists(fileName, section string) bool {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
return inifile.SectionExists(section)
}
func IniReadSectionIdents(fileName, section string) []string {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
return inifile.ReadSectionIdents(section)
}
func IniReadSections(fileName string) []string {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
return inifile.ReadSections()
}
func IniReadSectionValues(fileName, section string) []string {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
return inifile.ReadSectionValues(section)
}
func IniEraseSection(fileName, section string) {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
inifile.EraseSection(section)
}
func IniIdentExists(fileName, section, ident string) bool {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
return inifile.IdentExists(section, ident)
}
func IniDeleteIdent(fileName, section, ident string) {
inifile := NewIniFile(fileName, false)
defer inifile.Close()
inifile.DeleteIdent(section, ident)
}
而 Delphi 的 RTL 里提供有非常多的方便、实用、简洁的函数如 IntToStr、FileExists、IncludeTrailingBackslash 等等等等,我也打算慢慢地移植一些到 Golang,算是造点基础的轮子吧。
// Copyright 2016~2017 ecofast(无尽愿). All rights reserved.
// Use of this source code is governed by a BSD-style license.
// Package sysutils implements some useful system utility functions
// in the way of which Delphi(2007) RTL has done.
package sysutils
import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)
func CheckError(e error) {
if e != nil {
panic(e)
}
}
func BoolToStr(b bool) string {
if b {
return "1"
}
return "0"
}
func StrToBool(s string) bool {
if ret, err := strconv.ParseBool(s); err == nil {
return ret
}
return false
}
func FloatToStr(f float64) string {
return fmt.Sprintf("%g", f)
}
func GetApplicationPath() string {
path := filepath.Dir(os.Args[0])
return path + string(os.PathSeparator)
}
func DirectoryExists(path string) bool {
fileInfo, err := os.Stat(path)
if err == nil && fileInfo.IsDir() {
return true
}
return false
}
func FileExists(filename string) bool {
_, err := os.Stat(filename)
return err == nil || os.IsExist(err)
}
func CreateFile(filename string) bool {
// os.MkdirAll(path.Dir(filename))
_, err := os.Create(filename)
if err == nil {
return true
}
return false
}
func IncludeTrailingBackslash(path string) string {
if !strings.HasSuffix(path, string(os.PathSeparator)) {
return path + string(os.PathSeparator)
}
return path
}
最后再来个 litelog,基本照搬的 Go Recipes 里的代码。
// Copyright 2016~2017 ecofast(无尽愿). All rights reserved.
// Use of this source code is governed by a BSD-style license.
// Package litelog provides a logging infrastructure with an option
// to set the log level, log file and write log data into log file
package litelog
import (
"io"
"io/ioutil"
"log"
"os"
)
// holds the log level
type LogLvl int
const (
// logs nothing
LvlNone LogLvl = iota
// logs everything
LvlTrace
// logs Info, Warnings and Errors
LvlInfo
// logs Warnings and Errors
LvlWarning
// just logs Errors
LvlError
)
// package level variables which are pointers to log.Logger
var (
Trace *log.Logger
Info *log.Logger
Warning *log.Logger
Error *log.Logger
)
// initializes log.Logger objects
func initLogger(trace, info, warn, err io.Writer, flags int) {
flag := log.Ldate | log.Ltime | log.Lshortfile
if flags != 0 {
flag = flags
}
Trace = log.New(trace, "[Trace] ", flag)
Info = log.New(info, "[Info] ", flag)
Warning = log.New(warn, "[Warning] ", flag)
Error = log.New(err, "[Error] ", flag)
}
// Setup logging facilities
func Initialize(loglvl LogLvl, logflag int, logfile string) {
logFile, err := os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatalf("Error opening log file: %s", err.Error())
}
switch loglvl {
case LvlTrace:
initLogger(logFile, logFile, logFile, logFile, logflag)
return
case LvlInfo:
initLogger(ioutil.Discard, logFile, logFile, logFile, logflag)
return
case LvlWarning:
initLogger(ioutil.Discard, ioutil.Discard, logFile, logFile, logflag)
return
case LvlError:
initLogger(ioutil.Discard, ioutil.Discard, ioutil.Discard, logFile, logflag)
return
default:
initLogger(ioutil.Discard, ioutil.Discard, ioutil.Discard, ioutil.Discard, logflag)
logFile.Close()
return
}
}
这是示例代码和执行结果。
// litelogdemo project main.go
package main
import (
"errors"
"flag"
"litelog"
"github.com/ecofast/sysutils"
)
func main() {
loglvl := flag.Int("loglvl", 0, "an integer value(0--4)")
flag.Parse()
litelog.Initialize(litelog.LogLvl(*loglvl), 0, sysutils.GetApplicationPath()+"logs.txt")
litelog.Trace.Println("=====Main started=====")
test()
err := errors.New("Sample error")
litelog.Error.Println(err.Error())
litelog.Trace.Println("=====Main completed=====")
}
func test() {
litelog.Trace.Println("Test started")
for i := 0; i < 10; i++ {
litelog.Info.Println("Counter value is:", i)
}
litelog.Warning.Println("The counter variable is not being used")
litelog.Trace.Println("Test completed")
}


浙公网安备 33010602011771号