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")
}

 

posted @ 2017-01-05 19:47  ecofast  阅读(1483)  评论(0编辑  收藏  举报