實作練習-ini解析器
尚硅谷Golang課

用來解析.ini文檔格式是否有問題

package main

import (
	"fmt"
	"io/ioutil"
	"reflect"
	"strings"
)

type RedisConfig struct {
	Host     string `ini:"host"`
	Port     int    `ini:"port"`
	Password string `ini:"password"`
	Database int    `ini:"database"`
}
type MysqlConfig struct {
	Address  string `ini:"address"`
	Port     int    `ini:"port"`
	Password string `ini:"password"`
	Username string `ini:"username"`
}

type Config struct {
	MysqlConfig `ini:"mysql"`
	RedisConfig `ini:"redis"`
}

func loadIni(fileName string, data interface{}) (err error) {
	// 0. 參數校驗
	// 0.1 傳來的data必須是結構體類型指針
	t := reflect.TypeOf(data)
	// fmt.Println(t.Kind())
	if t.Kind() != reflect.Ptr {
		err = fmt.Errorf("傳來的data必須是指針")
		return
	}
	if t.Elem().Kind() != reflect.Struct {
		err = fmt.Errorf("傳來的data必須是結構體類型指針")
		return
	}
	// 1. 讀取文件~>Byte類型
	b, err := ioutil.ReadFile(fileName)
	if err != nil {
		return
	}
	//轉成字符串
	lineSlice := strings.Split(string(b), "\r\n")
	// fmt.Println(lineSlice)
	// 2. 一行一行讀取數據
	var structName string
	for idx, line := range lineSlice {
		line = strings.TrimSpace(line) //去除多餘空格
		// 2.1 如果是註釋就跳過
		if strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") {
			continue
		}
		//純空行就跳過
		if len(line) == 0 {
			continue
		}
		// 2.2 如果是[]開頭表示是節
		if strings.HasPrefix(line, "[") {
			if line[0] != '[' || line[len(line)-1] != ']' {
				err = fmt.Errorf("line:%d syntax error", idx+1)
				return
			}
			//去掉這行的首尾跟空格拿到內容
			section := strings.TrimSpace(line[1 : len(line)-1])
			if len(section) == 0 {
				err = fmt.Errorf("line:%d syntax error", idx+1)
				return
			}
			//根據section去data反射對應的結構體
			for i := 0; i < t.Elem().NumField(); i++ {
				field := t.Elem().Field(i)
				if section == field.Tag.Get("ini") {
					//說明找到了,把字段名記下
					structName = field.Name
					fmt.Println(section, structName)
				}

			}

		} else {
			// 2.3 剩下的就是分割的鍵值對
			//先排除異常的行
			if strings.Index(line, "=") == -1 || strings.HasPrefix(line, "=") {
				err = fmt.Errorf("line:%d syntax error", idx+1)
				return
			}
			index := strings.Index(line, "=")
			key := strings.TrimSpace(line[:index])
			// value := strings.TrimSpace(line[:index+1])
			v := reflect.ValueOf(data)
			sValue := v.Elem().FieldByName(structName) //拿到嵌套結構體的值信息
			sType := sValue.Type()                     //拿到嵌套結構體的類型信息
			if sType.Kind() != reflect.Struct {
				err = fmt.Errorf("%s應該為一個結構體", structName)
				return
			}
			var fieldName string
			//遍歷嵌套結構體每一個字段 判斷tag是否=key
			for i := 0; i < sValue.NumField(); i++ {
				field := sType.Field(i)
				if field.Tag.Get("ini") == key {
					fieldName = field.Name
					break
				}
			}
			//根據fieldName取出字段賦值
			fileObj := sValue.FieldByName(fieldName)
			fmt.Println(fieldName, fileObj)
		}

	}
	return
}

func main() {
	var cfg Config
	err := loadIni("C:/config.ini", &cfg)
	if err != nil {
		panic(err)
	}
}

上次修改於 2021-08-01

此篇文章的評論功能已經停用。