zoukankan      html  css  js  c++  java
  • 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]
    ", sec))
    		for _, s := range lst {
    			buf.WriteString(fmt.Sprintf("%s
    ", s))
    		}
    		buf.WriteString("
    ")
    	}
    	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]
    ", sec))
    		sysutils.CheckError(err)
    
    		for _, s := range lst {
    			_, err = fw.WriteString(fmt.Sprintf("%s
    ", s))
    			sysutils.CheckError(err)
    		}
    
    		_, err = fw.WriteString("
    ")
    		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")
    }
    

  • 相关阅读:
    理解Objective-C Runtime (六)super
    理解Objective-C Runtime (五)协议与分类
    理解Objective-C Runtime(四)Method Swizzling
    理解Objective-C Runtime(三)消息转发机制
    Objective-C Runtime(二)消息传递机制
    matlab数学实验--第一章
    Python之json模块
    Python之os模块和sys模块
    Python之小练习
    vuedevtools 离线安装
  • 原文地址:https://www.cnblogs.com/ecofast/p/6253694.html
Copyright © 2011-2022 走看看