編程基礎 0002_名庫講解

名庫講解

goconfig

go 語言針對 windows 下常見的 ini 格式的配置文件解析器,該解析器在涵蓋了所有 ini 文件操作的基礎上,又針對 go 語言實際開發過程中遇到的一些需求進行了擴展。該解析器最大的優勢在於對注釋的極佳支持,除此之外,支持多個配置文件覆蓋加載也是非常特別但好用的功能。

  • 提供與 windows api 一模一樣的操作
  • 支持遞歸讀取分區
  • 支持自增鍵名
  • 支持對注釋的讀與寫操作
  • 支持直接返回指定類型的鍵值
  • 支持多個文件覆蓋加載

安裝

  • gopm
  • gopm get github.com/Unknwon/goconfig
  • go get
  • go get github.com/Unknwon/goconfig
  • doc

基本使用方法

; conf.ini
; Google
google = www.google.com
search = http://%(google)s

; Here are Comments
; Second line
[Demo]
# This symbol can also make this line to be comments
key1 = Let's us goconfig!!!
key2 = test data
key3 = this is based on key2:%(key2)s
quote = "special case for quote
"key:1" = This is the value of "key:1"
"key:2=key:1" = this is based on "key:2=key:1" => %(key:1)s
中國 = China
chinese-var = hello %(中國)s!
array_key = 1,2,3,4,5
[What's this?]
; Not Enough Comments!!
name = try one more value ^-^
empty_value =

[url]
google_fake = www.google.fake
google_url =  http://%(google_fake)s

[parent]
name = john
relation = father
sex = male
age = 32
money = 1.25

[parent.child]
age = 3
married = true

[parent.child.child]

; Auto increment by setting key to "-"
[auto increment]
- = hello
- = go
- = config
package main

import (
 "github.com/Unknwon/goconfig"
)

// 加載配置文件
cfg,err:=goconfig.LoadConfigFile("conf.ini")
if err!=nil {
    log.Fatalf("無法加載配置文件:%s",err)
}
// 基本讀寫操作
value,err:=cfg.GetValue(goconfig.DEFAULT_SECTION,"key_default")
isInsert:=cfg.SetValue(goconfig.DEFAULT_SECTION,"key_devault","這是新的值")
package main

import (
  "fmt"
  "github.com/Unknwon/goconfig"
  "log"
)

func main() {
  cfg,err := goconfig.LoadConfigFile("conf.ini")
  if err!=nil {
    log.Fatalf("無法加載配置文件:%s",err)
  }
  value,err:=cfg.GetValue("Demo","key1")
  if err!=nil {
    log.Fatalf("無法獲取值{%s}:%s","Demo",err)
  }
  fmt.Printf("%s -> %s : %s","Demo","key1",value)
}
// 注釋 分區注釋 鍵注釋
comment:=cfg.GetSectionComments("super") //獲取分區注釋
log.Printf("分區 %s 的注釋:%s","super",comment)
v:=cfg.SetSectionComments("super","# 這是新的分區注釋") // 井號保留
  • 類型轉換讀取
  • vInt,err:=cfg.Int("must","int") //分區 鍵值
  • Must 系列方法
  • vBool:=cfg.MustBool("must","bool")
  • 刪除指定鍵值
  • ok:=cfg.DeleteKey("must","string")
  • 保存配置文件
  • err=goconfig.SaveConfigFile(cfg,"conf_save.ini")

高級使用

  • 多文件覆蓋加載
  • cfg,err:=goconfig.LoadConfigFile("conf.ini","conf2.ini")
  • err = cfg.AppendFiles("conf3.ini")
  • 配置文件重載
  • err = cfg.Reload()
  • 它會按之前的多個順序來加載
  • 為 must 系統方法設置缺省值
  • vBool:=cfg.MustBool("must","bool404",true)
  • 遞歸讀取鍵值
  • 子分區只設置值不同的相同的不用設置(子分區用.來分隔)
  • 自動鍵名獲取,獲取整個分區 (自增鍵名用-來設置
  • sec,err:=cfg.GetSection("auto increment")
; 鍵值必須是先聲明過的,使用%(鍵名)s
google=www.google.com
search=http://%(goole)s

xorm 基本用法

  • 結構與數據表的雙向映射與增量同步
  • 方法調用鏈式操作
  • session 事務與回滾
  • ORM 方法與純 SQL 混合事務
  • 基於 LRU 規則的緩存器
  • 數據表樂觀鎖
gopm get github.com/go-xorm/xorm
# 或使用go get安裝
go get github.com/go-xorm/xorm

go 語言中的 ORM 習慣直接在定義結構的基礎上配合 tag 來定義模型

type Account struct {
    Id int64
    Name string `xorm:"unique"` // tag
    Balance float64
    Version int `xorm:"version"` //樂觀鎖
}

新建兩個文件main.gomodels.go文件

// main.go
/* 1. 導入數據庫驅動包 這裡以sqlite為例
      a. go get github.com/go-xorm/xorm
      b. go get github.com/mattn/go-sqlite3
      c. 導入驅動包 import(
          _ "github.com/mattn/go-sqlite3" // 因為導入的包必須使用所以要用個佔位,只執行這個包的初始化函數
      )
*/
package main

import (
    "fmt"
    "xorm"
)

const prompt = `Please enter number of operation:
1. Create new account
2. Show detail of account
3. Deposit
4. Withdraw
5. Make transfer
6. List account by Id
7. List account by balance
8. Delete account
9. Exit`

func main() {
    fmt.Println("Welcome bank of xorm!")
Exit:
    for {
        fmt.Println(prompt)
        var num int
        fmt.Scanf("%dn", &num) //回車
        switch num {
        case 1:
            fmt.Println("Place enter <name> <balance>:")
            var name string
            var balance float64
            fmt.Scanf("%s %fn", &name, &balance)
            if err := xorm.NewAccount(name, balance); err != nil {
                fmt.Println(err)
            }
        case 2:
            fmt.Println("Place enter <id>:")
            var id int64
            fmt.Scanf("%dn", &id)
            a, err := xorm.GetAccount(id)
            if err != nil {
                fmt.Println(err)
            } else {
                fmt.Printf("%#v", a)
            }
        case 3:
            fmt.Printf("Please enter <id> <deposit>: ")
            var id int64
            var deposit float64
            fmt.Scanf("%d %fn", &id, &deposit)
            a, err := xorm.MakeDeposit(id, deposit)
            if err != nil {
                fmt.Println(err)
            } else {
                fmt.Printf("%#v", a)
            }
        case 4:
            fmt.Printf("Please enter <id> <withdraw>: ")
            var id int64
            var withdraw float64
            fmt.Scanf("%d %fn", &id, &withdraw)
            a, err := xorm.MakeWithdraw(id, withdraw)
            if err != nil {
                fmt.Println(err)
            } else {
                fmt.Printf("%#v", a)
            }
        case 5:
            fmt.Printf("Please enter <id> <balance> <id>")
            var id1, id2 int64
            var balance float64
            fmt.Scanf("%d %f %d", &id1, &balance, &id2)
            if err := xorm.MackTransfer(id1, id2, balance); err != nil {
                fmt.Println(err)
            }
        case 6:
            as, err := xorm.GetAccountAscId("id")
            if err != nil {
                fmt.Println(err)
            } else {
                for i, a := range as {
                    fmt.Printf("%d %#vn", i, a)
                }
            }
        case 7:
            as, err := xorm.GetAccountAscId("balance")
            if err != nil {
                fmt.Println(err)
            } else {
                for i, a := range as {
                    fmt.Printf("%d %#vn", i, a)
                }
            }
        case 8:
            fmt.Println("Place enter <id>:")
            var id int64
            fmt.Scanf("%dn", &id)
            if err := xorm.DeleteAccount(id);err!=nil {
                fmt.Println(err)
            }
        case 9:
            break Exit

        }
    }
}
// models.go
/*
注意:每個golang源文件中都可以定義一個init函數。golang系統中,所有的源文件都有自己所屬的目錄,每一個目錄都有對應的包名。在包的引用中,一旦某一個包被使用,則這個包下邊的init函數將會被執行,且只執行一次。
1. 新建
_,err:=x.Insert(&Account{Name:name,Balance:balance})
2. 刪除
_,err:=
3. 獲取並修改
a:=&Account{}
has,err:=x.Id(id).Get(a)
a.Balance+=deposit
_,err = x.Update(a)
4. 原始的事務操作 通過version判斷。
*/
package xorm

import (
    "errors"
    "github.com/go-xorm/xorm"
    _ "github.com/mattn/go-sqlite3"
    "log"
)

type Account struct {
    Id      int64  //默認主鍵 如果指定其它為主鍵則要加tag `xorm:"pk"`
    Name    string //`xorm:"unique"` // tag
    Balance float64
    Version int `xorm:"version"` //樂觀鎖
}

var x *xorm.Engine

func init() {
    var err error
    x, err = xorm.NewEngine("sqlite3", "./bank.db") // xorm.NewEngine{"sqlite3","./bank.db"}
    if err != nil {
        log.Fatalf("Fail to create engine %v", err)
    }
    if err = x.Sync(new(Account)); err != nil {
        log.Fatalf("Fal to sync database: %v", err)
    }
}

func NewAccount(name string, balance float64) error {
    //新增記錄
    _, err := x.Insert(&Account{Name: name, Balance: balance})
    return err
}

// 獲取記錄
func GetAccount(id int64) (*Account, error) {
    a := &Account{} //
    has, err := x.Id(id).Get(a)
    if err != nil {
        return nil, err
    } else if !has {
        return nil, errors.New("Account not found")
    }
    return a, nil
}

//存款
func MakeDeposit(id int64, deposit float64) (*Account, error) {
    //a := &Account{}
    a, err := GetAccount(id)
    if err != nil {
        return nil, err
    }
    a.Balance += deposit
    _, err = x.Update(a)
    return a, err
}

//取款
func MakeWithdraw(id int64, withDraw float64) (*Account, error) {
    //a := &Account{}
    a, err := GetAccount(id)
    if err != nil {
        return nil, err
    }
    if a.Balance <= withDraw {
        return nil, errors.New("Not enough balance")
    }
    a.Balance -= withDraw
    _, err = x.Update(a)
    return a, err
}

//轉帳
func MackTransfer(id1, id2 int64, balance float64) error {
    a1, err := GetAccount(id1)
    if err != nil {
        return err
    }
    a2, err := GetAccount(id2)
    if err != nil {
        return err
    }
    if a1.Balance < balance {
        return errors.New("Not enough balance")
    }
    a1.Balance -= balance
    a2.Balance += balance
    if _, err = x.Update(a1); err != nil {
        return err
    } else if _, err = x.Update(a2); err != nil {
        return err
    }
    return nil
}

//批量獲取
/*
  err = x.Desc("balance").Find(&as) //入一個slice的地址
*/
func GetAccountAscId(orderBy string) (as []*Account, err error) {
    err = x.Asc(orderBy).Find(&as)
    return as, err
}

//delete
func DeleteAccount(id int64) error {
    _, err := x.Delete(&Account{Id: id})
    return err
}

xorm 高級用法

事務及回滾

  • 創建 session 對象
  • 啓動事務
  • 通過 session 操作數據庫
  • 發生錯誤時進行回滾
  • 提交事務
// 1.
sess:=x.NewSession()
// 2.
sess.Begin()
// 3.
sess.Update()
// 4.
sess.RollBack()
// 5.
sess.Commit()

修改上例中的轉帳操作

//轉帳
func MackTransfer(id1, id2 int64, balance float64) error {
    a1, err := GetAccount(id1)
    if err != nil {
        return err
    }
    a2, err := GetAccount(id2)
    if err != nil {
        return err
    }
    if a1.Balance < balance {
        return errors.New("Not enough balance")
    }
    a1.Balance -= balance
    a2.Balance += balance

    //if _, err = x.Update(a1); err != nil {
    //    return err
    //} else if _, err = x.Update(a2); err != nil {
    //    return err
    //}
    //return nil
    // 創建session
    sess:=x.NewSession()
    defer sess.Close() //當函數執行完或出錯要釋放資源
    // 開始事務
    if err=sess.Begin();err!=nil {
        return  err
    }
    // 開始事務的更新操作
    if _, err = sess.Update(a1); err != nil {
        sess.Rollback()
        return err
    } else if _, err = sess.Update(a2); err != nil {
        sess.Rollback()
        return err
    }
    // 提交事務
    return sess.Commit()
}

統計記錄條數

x.Count(new(Accout))
// 配合鏈式操作
x.Where("id>10").Count(new(Account))

迭代查詢

  • 迭代查詢某個表中符合條件的所有記錄
  • 使用 Rows 對象
// 迭代查詢
x.Iterate(new(Account),func(idx int,bean interface{}){
    fmt.Printf("%d,%#vn",idx,bean.(*Account))
})
// Rows
func GetRows() {
    a := new(Account)
    rows, err := x.Rows(new(Account))
    if err != nil {
        log.Fatalf("Fail to get row:%vn", err)
    }
    defer rows.Close()
    for rows.Next() {
        if err = rows.Scan(a); err != nil {
            log.Fatalf("Fail to get row:%vn", err)
        }
        fmt.Printf("%#vn", a)
    }
}
// main.go
package main

import (
    "fmt"
    "log"
    "xorm"
)

type Account xorm.Account



func main() {
    fmt.Println("Welcome to bank of xorm2!")
    count, err := xorm.GetNum()
    if err != nil {
        log.Fatalf("Fail to get account count: %vn", err)
    }
    fmt.Println("Account count: ", count)
    for i := count; i < 10; i++ {
        if err = xorm.NewAccount(fmt.Sprintf("joe%d", i), float64(i)*100); err != nil {
            log.Fatalf("Fail to create account %vn", err)
        }
    }
    //    迭代查詢
    fmt.Println("Query all recores:")
    xorm.It()
    fmt.Println("==== Query all recores by Rows:====")
    xorm.GetRows()

}
// models.go /src/xorm/models.go 新增這幾個方法
// Iterator 迭代
func It() {
    x.Iterate(new(Account), printFn)
}

var printFn = func(idx int, bean interface{}) error {
    fmt.Printf("%d:%vn", idx, bean.(*Account))
    return nil
}

func GetRows() {
    a := new(Account)
    rows, err := x.Rows(new(Account))
    if err != nil {
        log.Fatalf("Fail to get row:%vn", err)
    }
    defer rows.Close()
    for rows.Next() {
        if err = rows.Scan(a); err != nil {
            log.Fatalf("Fail to get row:%vn", err)
        }
        fmt.Printf("%#vn", a)
    }
}

常用查詢方法

  • 當只需要結構中的某個字段的值對你有價值時
  • x.Cols("name").Iterate(new(Account),...)
  • 當希望刻意忽略某個字段的查詢結構時
  • x.Omit("name").Iterate(new(Account)...)
  • 查詢結果偏移,這在分頁應用中最為常見
  • x.Limit(3,2).Iterate(new(Account),...)
func GetNameCol()  {
    x.Cols("name").Iterate(new(Account),printFn)
}

func ExcludeCol()  {
    x.Omit("name").Iterate(new(Account),printFn)
}

日誌功能

  • 開啓日誌
  • x.ShowSQL = true
  • 將日誌保存到文件
    f,err:=os.Create("sql.log")
    if err!=nil {
        log.Fatalf("Fail to create log file:%vn",err)
        return
    }
    xorm.NewSimpleLogger(f)
    logger:= xorm.NewSimpleLogger(f)
    logger.ShowSQL(true) // 和教程不一樣,這塊需求注意
    x.SetLogger(logger)

LRU 緩存

設置默認 LRU 緩存

cacher:=xorm.NewLRUCacher(xorm.NewMemoryStore(),1000)
x.SetDefaultCacher(cacher)

事件鈎子

官方提供了 6 類

  • BeforeInsert
  • AfterInsert
  • BeforeUpdate
  • AfterUpdate
  • BeforeDelete
  • AfterDelete
// 作為方法調用
func (a *Account) BeforeInsert(){
    log.Printf("before insert %s", a.Name)
}
func (a *Account) AfterInsert(){
    log.Printf("after insert %s", a.Name)
}

著名案例 Gogs Go Walker GoBuild.IO Sudo China

GoConvery

  • 測試代碼優雅簡潔
  • 直接集成 Go 原生測試
  • 全自動編譯測試
  • 詳細展示測試結構及覆蓋率
  • 高可讀性的命令行輸出結果
  • 半自動化書寫測試

GoConvery 安裝

  • gopm 安裝
  • gopm get github.com/smartystreets/goconvey
  • go get
  • go get github.com/smartystreets/goconvey

通過簡單的示例來實現四則運算的 4 個函數,來使用測試

// main.go 一般不會命名為main包,可做單元測試的包一般都是可做重復利用的包,一般情況下main包不太可能會做重復利用
package goconvey

import "errors"

func Add(a, b int) int {
    return a + b
}
func Substract(a, b int) int {
    return a - b
}

func Multiply(a, b int) int {
    return a * b
}

func Division(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("被除數為0!")
    }
    return a / b, nil
}

編寫測試

//引入兩個包 testing和github.com/smartystreets/goconvey/convey
package goconvey

import (
    . "github.com/smartystreets/goconvey/convey"
    "testing"
)

func TestAdd(t *testing.T) {
    Convey("將兩數相加", t, func() {
        So(Add(1, 2), ShouldEqual, 3)
    })
}

func TestSubstract(t *testing.T) {
    Convey("將兩數相減", t, func() {
        So(Substract(1, 2), ShouldEqual, -1)
    })
}
func TestMultiply(t *testing.T) {
    Convey("將兩數相乘", t, func() {
        So(Multiply(3, 2), ShouldEqual, 6)
    })
}

func TestDivision(t *testing.T) {
    Convey("將兩數相除", t, func() {
        //    嵌套Convery 除0和不除0情況
        //注意嵌套的不用傳t參數
        Convey("被除數為0", func() {
            _, err := Division(10, 0)
            So(err, ShouldNotBeNil)
        })
        Convey("除數不為0", func() {
            num, err := Division(10, 2)
            So(err, ShouldBeNil)
            So(num, ShouldEqual, 5)
        })
    })
}

測試方法有兩種,

一種是使用 go test 如下圖

編程基礎 0002_名庫講解

Convey有幾個就幾個點,也可以使用go test -v 內容會更豐富一下些

另外一種安裝測試覆蓋工具

  • go get code.google.com/p/go.tools/cmd/cover
  • 測試目錄下輸入goconvey 注意 gopath 的設置。

Macaron Web 框架

  • 支持靈活多變的路由規則和組合模式
  • 支持無限路由緩和無限嵌套
  • 支持直接集成現有的服務
  • 支持運行時動態設置需要渲染的模板集
  • 支持對模塊的輕鬆接入與解除
  • 提供便利的依賴注入服務
  • 採用更好的路由層和更少的反射來提升執行速度

  • gopm 安裝

  • gopm get github.com/Unknwon/macaron
  • 通過 go get 安裝
  • go get github.com/Unknwon/macaron

官網

Macaron 框架是從 Gogs 項目中提煉發展而來的,因為在開發 Gogs 的過程中產生了大量輔助模塊可以被重復利用到其它項目中,因此才萌發了創建一個新的框架來提高代碼的利用率的想法

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/6715

(0)
Walker的頭像Walker
上一篇 2026年3月8日 15:11
下一篇 2026年3月9日 12:56

相關推薦

  • 編程基礎 0001_基礎教程

    go 甚麼是 Go是一門併發支持、垃圾加收的編譯型系統編程語言,具有靜態編譯語言的高性能和動態語言的,主要特點如下 類型安全和內存安全 以非常直觀和極低代價的方案實現高併發 高效的垃圾回收機制 快速編譯(同時解決了 C 語言中頭文件太多的問題) UTF-8 支持 安裝 源碼安裝 標準包安裝 第三方安裝 標準包安裝,一路下一步。安裝完後,會自動添加如下環境變量…

    後端開發 2026年3月6日
    6200
  • Go資深工程師講解(慕課) 000_課程目錄索引

    Google資深工程師深度講解Go語言 - 課程目錄索引 課程來源:慕課網(百度網盤備份)講師風格:從 Google 工程實踐出發,注重底層原理和工程規範 完整視頻章節與筆記對照表 章節 視頻文件 筆記位置 狀態 Ch1 課程介紹 1-1 課程導讀 — 跳過 1-2 安裝與環境 001.md > GOPATH、環境變量 已覆蓋 Ch2 基礎語法 2-1…

    後端開發 2026年3月6日
    5300
  • Go資深工程師講解(慕課) 004

    004 goroutine package main import ( "fmt" "time" ) func main() { for i:=0;i<10;i++{ go func(i int) { fmt.Printf("Hello from goroutine %d \n",i) // …

    後端開發 2026年3月6日
    5300
  • Go工程師體系課 004

    需求分析 後台管理系統 商品管理 商品列表 商品分類 品牌管理 品牌分類 訂單管理 訂單列表 用戶信息管理 用戶列表 用戶地址 用戶留言 輪播圖管理 電商系統 登錄頁面 首頁 商品搜索 商品分類導航 輪播圖展示 推薦商品展示 商品詳情頁 商品圖片展示 商品描述 商品規格選擇 加入購物車 購物車 商品列表 數量調整 刪除商品 結算功能 用戶中心 訂單中心 我的…

    2026年3月7日
    5400
  • Go工程師體系課 011

    查詢的倒排索引 1. 甚麼是倒排索引? 倒排索引(Inverted Index)是一種數據結構,用於快速查找包含特定詞彙的文檔。它是搜索引擎的核心技術之一。 1.1 基本概念 正排索引:文檔 ID → 文檔內容(詞列表) 倒排索引:詞 → 包含該詞的文檔 ID 列表 1.2 為甚麼叫"倒排"? 倒排索引將傳統的"文檔包含哪些詞"的關係倒轉為"詞出現在哪些文檔…

    後端開發 2026年3月6日
    6500
簡體中文 繁體中文 English