iniutils for Golang

  一直有计划将 Delphi 中的譬如 TIniFile 等相关功能移植到 Golang,这些设施在 Delphi 中(相对而言)比较常用,使用起来也非常方便。

  虽然 Github 上早已有这些三方库,但我还是想仿照 Delphi 的做法来实现一套,并额外提供直接干脆的调用接口等。

  代码已托管至 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
}

  

时间: 2024-12-18 10:46:00

iniutils for Golang的相关文章

golang []byte转string

golang中,字符切片[]byte转换成string最简单的方式是 package main import ( "fmt" _ "unsafe" ) func main() { bytes := []byte("I am byte array !") str := string(bytes) bytes[0] = 'i'//注意这一行,bytes在这里修改了数据,但是str打印出来的依然没变化, fmt.Println(str) } 打印信息:

golang实现Ringbuf

Ring buffer算法优点:高内存使用率,在缓冲buffer内存模型中,不太容易发生内存越界.悬空指针等 bug ,出了问题也容易在内存级别分析调试.做出来的系统容易保持健壮. package main import ( "bytes" "fmt" ) type Ringbuf struct { buf         []byte start, size int } func New(size int) *Ringbuf { return &Ringb

Golang Hash MD4

//Go标准包中只有MD5的实现 //还好,github上有MD4实现. package main import (     "golang.org/x/crypto/md4"     "encoding/hex"     "fmt" ) func get_md4(buf []byte) ([] byte) { ctx := md4.New() ctx.Write(buf) return ctx.Sum(nil) } func main() {

Java程序员的Golang入门指南(上)

Java程序员的Golang入门指南 1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如果有人说X语言比Y语言好,两方的支持者经常会激烈地争吵.如果你是某种语言老手,你就是那门语言的"传道者",下意识地会保护它.无论承认与否,你都已被困在一个隧道里,你看到的完全是局限的.<肖申克的救赎>对此有很好的注脚: [Red] These walls are funny.

golang学习笔记:golang 语法篇(二)

在语法篇(一)中学习了go中基本的数据类型.变量.常量等组成语言的基本要素,在这一节中将会学习如何将这些元素组织起来,最终写成可以执行的代码. 在这一部分包括: go中的流程控制语句: go中函数的用法: go特殊的错误处理方式: Golang中的流程控制语句 在具体编程的时候免不了需要使用一些特殊的语句实现某些功能,比如使用循环语句来进行迭代,使用选择语句控制程序的执行方式等.这些语句在任何一门程序设计语言 中都会有支持,golang中除了支持常用的循环,条件选择语句以外,还支持跳转语句,下面

Golang关键字—— if/else

Golang中,if/else 关键字用于条件判断,如果满足条件就做某事,否则做另一件事: if age >= 18 { fmt.Println("成年人") } else { fmt.Println("未成年") } 多重判断: if score >= 90 { fmt.Println("优秀") } else if score >= 70 { fmt.Println("良好") } else if sco

golang控制channel的出入口

golang控制channel的出入口 我们常常使用channel来在多个goroutine之间做数据通讯,但是chan作为函数的入参我们应该怎么写呢?也许有人觉得这个问题比较傻,不过这个还真的是我今天才知道的. 首先我们看看下面的代码: func main() { c := make(chan int) go in(c) go out(c) time.Sleep(time.Second) } func in(c chan int) { for i := 0; i < 10; i++ { c <

golang winForm开发

最近一直在看rust,语法挺头疼的,正好趁着1.0发布前的一段时间,回来玩玩golang. golang的语法很简单,liteIde又变得越来越好用,因此学习golang不会花费您多少时间,还能够清醒被rust晃晕的头脑,哈哈. winform开发虽然已经不再流行,但是用来练手却非常合适,写小工具也很爽,废话少说,golang的UI库就是大名鼎鼎的 andlabs ui, 在github的star数已达到1946,相当可观.这是一个跨平台的UI库,可以运行在 windows/linux/mac上

golang性能监控初探

最近在用golang写一个server.压力测试过程发现反应比较慢,但是由于中间的操作都是串行的,无法知道在哪个操作消耗了比较多时间. 一开始想到的是打log.但是单个请求又是很快的,于是想到如下方案 在调用每个函数的时候,统计该函数的时耗,然后利用channel把同一个函数调用发送到同一个地方,利用map进行累计统计(这里可以更近一步,比如统计每个worker甚至每个services的状态,包括最长请求时间,最短请求时间,平均消耗等等,如果加上runtime还可以记录其他的运行相关信息). 一