最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go打印結(jié)構(gòu)體提升代碼調(diào)試效率實(shí)例詳解

 更新時(shí)間:2024年02月01日 14:41:43   作者:波羅學(xué)?碼途漫漫  
這篇文章主要介紹了Go打印結(jié)構(gòu)體提升代碼調(diào)試效率實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

不知道大家是否遇到打印復(fù)雜結(jié)構(gòu)的需求?

結(jié)構(gòu)體的特點(diǎn)是有點(diǎn)像是一個(gè)盒子,但不同于 slice 與 map,它里面可以放很多不同類型的東西,如數(shù)字、字符串、slice、map 或者其他結(jié)構(gòu)體。

但,如果我們想看看盒子中內(nèi)容,該怎么辦呢?這時(shí)我們就要能打印結(jié)構(gòu)體了。

打印結(jié)構(gòu)體的能力其實(shí)挺重要的,它能幫我們檢查理解代碼,提高調(diào)試效率,確保代碼運(yùn)行正確。

本文讓我們以此為話題,聊聊 GO 語(yǔ)言中如何打印結(jié)構(gòu)體。這些方法,基本上同樣適用于其他復(fù)雜數(shù)據(jù)結(jié)構(gòu)

讓我們直接開始吧!

定義結(jié)構(gòu)體

首先,我們來(lái)定義一個(gè)結(jié)構(gòu)體,它會(huì)被接下來(lái)所有的方法用到。

代碼如下所示:

type Author struct {
    Name string
    Age  int8
    Sex  string
}

type Article struct {
    ID      int64
    Title   string
    Author  *Author
    Content string
}

我們定義了一個(gè) Article 結(jié)構(gòu)體用于表示一篇文章,內(nèi)部包含內(nèi)部實(shí)現(xiàn)了 IDTitle 和 Content 基礎(chǔ)屬性,還有一個(gè)字段是 Author 結(jié)構(gòu)體指針,用于保存作者信息。

我將先介紹四種基本的方式。

使用 fmt.Printf

最簡(jiǎn)單的方法是使用 fmt.Printf 函數(shù),如果希望顯示一些詳情,可和 %+v 格式化符號(hào)配合。這樣可以直接打印出結(jié)構(gòu)體的字段名和值。

我們可以這樣打印它,代碼如下:

func main() {
    article := Article{
        ID:      1,
        Title:   "How to Print a Structure in Golang",
        Author:  &Author{"poloxue", 18, "male"},
        Content: "This is a blog post",
    }
    fmt.Printf("%+v\n", article)
}

輸出:

{ID:1 Title:How to Print a Structure in Golang Author:0xc0000900c0 Content:This is a blog post}

如上所示,這段代碼會(huì)打印出 article 結(jié)構(gòu)體的所有字段值。不過,如果仔細(xì)觀察,會(huì)發(fā)現(xiàn)它的 Author 字段只打印了指針地址 - Author:0xc0000900c0 ,沒有輸出它的內(nèi)容。

這其實(shí)是符合預(yù)期的。*Author 是指針類型,它的值自然就是地址。

如果我就想打印 Author 字段的內(nèi)容,可通過用 fmt.Printf 打印指針實(shí)際指向內(nèi)容。

fmt.Print("%+v\n", article.Author)

輸出:

&{Name:poloxue Age:18 Sex:male}

我在測(cè)試的時(shí)候,發(fā)現(xiàn)個(gè)有趣的現(xiàn)象:

如果打印的是結(jié)構(gòu)體指針,它會(huì)自動(dòng)解引用,即能把內(nèi)容打印出來(lái)。如上的代碼所示,無(wú)論是

Printf("%+v\n", article.Author)

還是

Printf("%\n", *article.Author)

都能打印出結(jié)構(gòu)體的內(nèi)容。

但如果打印的結(jié)構(gòu)體,包含結(jié)構(gòu)體指針字段,則不會(huì)將內(nèi)容打印出來(lái),而只會(huì)打印地址,即指針值。

我猜測(cè),如此設(shè)計(jì)的原因是為了防止深層遞歸,或者循環(huán)引用。

想明白的話,似乎是個(gè)顯而易見的事情。

實(shí)現(xiàn) String 方法

除了以上將 Author 字段單獨(dú)拿出打印,我們還有其他方法實(shí)現(xiàn)嗎?當(dāng)然有,這就是本節(jié)要說(shuō)的 - 實(shí)現(xiàn) String 方法。

這其實(shí)是 Go 提供的一種機(jī)制,一個(gè)類型如果滿足 Stringer 接口,即實(shí)現(xiàn)了 String 方法,打印時(shí)返回的就是 String 方法的返回內(nèi)容。

Stringer 定義如下:

type Stringer interface {
    String() string
}

當(dāng)我們使用 fmt.Printf 打印結(jié)構(gòu)體時(shí),就會(huì)調(diào)用定義的 String 方法,控制結(jié)構(gòu)體的輸出格式。例如:

func (a Article) String() string {
    return fmt.Sprintf("Title: %s, Author: %s, Content: %s", a.Title, a.Author.Name, a.Content)
}

func main() {
    article := Article{
        ID:      1,
        Title:   "How to Print a Structure in Golang",
        Author:  &Author{"poloxue", 18, "male"},
        Content: "This is a blog post",
    }
    fmt.Println(article)
}

輸出:

Title: How to Print a Structure in Golang, Author: poloxue, Content: This is a blog post

檢查結(jié)果,的確是 String 方法中定義的形式?,F(xiàn)在,我們可以隨心所欲定義打印格式了。

這種方式還有一個(gè)特點(diǎn),就是性能高。畢竟,它沒有任何啰嗦,直接到拿到結(jié)果。

到這里,我們已經(jīng)有能力打印結(jié)構(gòu)體了。但這里也有些缺點(diǎn)。

首先,不夠美觀,輸出結(jié)構(gòu)易讀性差。這不利于快速定位。

其次,每次都要自定義輸出。如果只是為了 debug 調(diào)試代碼,而不是功能代碼,希望有更方便方式直接打印出所有內(nèi)容。

json.MarshalIndent

首先,如何實(shí)現(xiàn)美化輸出呢?

如果你想要一個(gè)更美觀的輸出格式,最便捷的方式,可使用標(biāo)準(zhǔn)庫(kù) encoding/json 的 json.MarshalIndent 函數(shù),它會(huì)將結(jié)構(gòu)體轉(zhuǎn)換為 JSON 格式,且能控制縮進(jìn),使輸出更易于閱讀。

示例代碼,如下所示:

import (
    "encoding/json"
    "fmt"
)

func main() {
    article := Article{
        ID:      1,
        Title:   "How to Print a Structure in Golang",
        Author:  &Author{"poloxue", 18, "male"},
        Content: "This is a blog post",
    }
    articleJSON, _ := json.MarshalIndent(article, "", "    ")
    fmt.Println(string(articleJSON))
}

如上的代碼中,json.MarshalIndent 的第三個(gè)參數(shù)表示縮進(jìn)的大小。我們看下代碼的輸出吧。

輸出:

{
    "ID": 1,
    "Title": "How to Print a Structure in Golang",
    "Author": {
        "Name": "poloxue",
        "Age": 18,
        "Sex": "male"
    },
    "Content": "This is a blog post"
}

以這樣美觀的 JSON 格式打印的 Article 結(jié)構(gòu)體,明顯易讀了許多。

reflect 包打印復(fù)雜結(jié)構(gòu)

如果想完全控制結(jié)構(gòu)體打印,還可使用 reflect 包。它不僅僅是可以拿到 Go 變量的值,其他信息,如結(jié)構(gòu)體的字段名和類型,都可輕而易舉拿到。

這也是為什么可通過 reflect 包能最大粒度控制輸出格式。

示例代碼,如下所示:

import (
    "fmt"
    "reflect"
)

func main() {
    article := Article{
        ID:      1,
        Title:   "How to Print a Structure in Golang",
        Author:  &Author{"poloxue", 18, "male"},
        Content: "This is a blog post",
    }

    val := reflect.ValueOf(article)
    for i := 0; i < val.NumField(); i++ {
        field := val.Type().Field(i)
        fmt.Printf(
            "Type: %v, Field: %s, Value: %v\n",
            field.Type,
            field.Name,
            val.Field(i),
        )
    }
}

輸出:

Type: int64, Field: ID, Value: 1
Type: string, Field: Title, Value: How to Print a Structure in Golang
Type: *main.Author, Field: Author, Value: &{poloxue 18 male}
Type: string, Field: Content, Value: This is a blog post

我們輸出了字段類型、名稱和值。

當(dāng)然,reflect 提供了靈活性,但具體的打印格式,我們就要自己按需求自行定義。前面介紹的 Printf 函數(shù),內(nèi)部實(shí)現(xiàn)本質(zhì)上也依賴了 reflect 。

如果想要深度打印信息,即使是指針類型字段,也可通過 reflect 繼續(xù)深度打印。

代碼類似于:

if field.Type.Kind() == reflect.Pointer {
    fmt.Println(val.Field(i).Elem())
}

即如果是指針類型,通過 Elem() 繼續(xù)深入它的內(nèi)部。

當(dāng)然,如果你希望得到結(jié)構(gòu)體類型相關(guān)信息。reflect 甚至可以在結(jié)構(gòu)體沒有實(shí)例化打印其類型的詳情。

func printStructType(t reflect.Type) {
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Printf("%s: %s\n", field.Name, field.Type)
    }
}

func main() {
    t := reflect.TypeOf((*Article)(nil)).Elem()
    printStructType(t)
}

核心就是那句 (*Article)(nil) 得到一個(gè)類型為 *Article 的 nil。也算是類型內(nèi)存空間的占用。

性能壓測(cè)

我嘗試了不同的打印方法后,也進(jìn)行了一個(gè)簡(jiǎn)單的性能測(cè)試。

測(cè)試結(jié)果如下所示:

BenchmarkFmtPrintf-16              2631248     447.3 ns/op
BenchmarkJSONMarshalIndent-16       997448      1016 ns/op
BenchmarkCustomStringMethod-16     5135541     225.5 ns/op
BenchmarkReflection-16             2030233     594.9 ns/op

測(cè)試結(jié)果顯示,使用自定義的 String 方法是最快的,而 json.MarshalIndent 則是最慢的。

這意味著如果你關(guān)心程序的運(yùn)行速度,最好使用自定義的String方法來(lái)打印結(jié)構(gòu)體。

這里單獨(dú)提醒一點(diǎn),因?yàn)?nbsp;fmt.Printf 的內(nèi)部是使用反射,所以要能測(cè)試出 String() 自定義的效果,內(nèi)部實(shí)現(xiàn)就不要用 fmt.Sprintf 等方法格式化字符,而是推薦使用 strconv 中的一些函數(shù)。

示例代碼:

func (a Article) String() string {
    return "{ID:" + strconv.Itoa(int(a.ID)) + ", Title:" + a.Title + ",AuthorName:" + a.Author.Name + "}"
}

這樣才能真正意義上測(cè)試出 String 自定義的優(yōu)勢(shì)。不靠套娃,最終得到用了 String 等于沒用的效果。

如果想知道為什么 strconv 更快,可閱讀我之前的一篇文章:GO 中高效 int 轉(zhuǎn)換 string 的方法與源碼剖析

三方庫(kù)

前面介紹了 4 種打印結(jié)構(gòu)內(nèi)容的方案,。特別是第四種,提供了最大化的自由度。但缺點(diǎn)是要自定義,非常麻煩。

接下來(lái),我嘗試推薦一些好用的三方庫(kù),它們將我們常用的一些模式實(shí)踐成庫(kù),便于我們使用。

go-spew

我們首先來(lái)看看一個(gè)叫做 go-spew 的第三方庫(kù)。

這個(gè)庫(kù)提供了深度打印 Go 數(shù)據(jù)結(jié)構(gòu)的功能,對(duì)于調(diào)試非常有用。它可以遞歸地打印出結(jié)構(gòu)體的字段,即使是嵌套的結(jié)構(gòu)體也能打印出來(lái)。

例如:

import "github.com/davecgh/go-spew/spew"

func main() {
    article := Article{
        ID:      1,
        Title:   "How to Print a Structure in Golang",
        Author:  &amp;Author{"poloxue", 18, "male"},
        Content: "This is a blog post",
    }
    spew.Dump(article)
}

這樣會(huì)詳細(xì)地打印出 article 結(jié)構(gòu)體的所有內(nèi)容。

輸出如下:

(main.Article) {
 ID: (int64) 1,
 Title: (string) (len=34) "How to Print a Structure in Golang",
 Author: (*main.Author)(0xc000100330)({
  Name: (string) (len=7) "poloxue",
  Age: (int8) 18,
  Sex: (string) (len=4) "male"
 }),
 Content: (string) (len=19) "This is a blog post"

可以看出,上面的輸出內(nèi)容包含的信息非常豐富。

如果希望自定義打印格式,可通過 spew 提供的 ConfigState 配置,如縮進(jìn),打印深度。

示例代碼:

// 設(shè)置 spew 的配置
spewConfig := spew.ConfigState{
    Indent:                  "\t", // 索引為 Tab
    DisableMethods:          true,
    DisablePointerMethods:   true,
    DisablePointerAddresses: true,
    MaxDepth:                1, // 設(shè)置打印深度為 1
}

spewConfig.Dump(article)

輸出:

(main.Article) {
    ID: (int64) 1,
    Title: (string) (len=34) "How to Print a Structure in Golang",
    Author: (*main.Author)({
        <max depth reached>
    }),
    Content: (string) (len=19) "This is a blog post"
}

因?yàn)?,我將打印深度配置?1,可以看到 Author 的字段的內(nèi)容是沒有打印的。

更多能力可自行探索。

pretty

除了 go-spew,還有一些沒那么強(qiáng)大,但也還不錯(cuò)的庫(kù),方便我們調(diào)試復(fù)雜數(shù)據(jù)結(jié)構(gòu),如 pretty[1]。

import (
    "fmt"
    "github.com/kr/pretty"
)

func main() {
  // 省略 ...
  fmt.Printf("%# v\n", pretty.Formatter(article))
}

輸出:main.Article{
    ID:      1,
    Title:   "How to Print a Structure in Golang",
    Author:  &main.Author{Name:"poloxue", Age:18, Sex:"male"},
    Content: "This is a blog post",
}

輸出結(jié)果為格式化的結(jié)構(gòu)體輸出。

這兩個(gè)庫(kù)都已經(jīng)處于很久不更新的狀態(tài),但是功能滿足我們的需求。

結(jié)語(yǔ)

本文主要介紹了 Go 語(yǔ)言中打印結(jié)構(gòu)體的不同方法。我們從簡(jiǎn)單的 fmt.Printf 到使用反射,甚至是第三方庫(kù),我們是有很多選擇。

簡(jiǎn)單主題深入起來(lái),擴(kuò)展內(nèi)容也可很豐富。

關(guān)于打印結(jié)構(gòu)體這個(gè)主題,還有一個(gè)部分沒有談到,就是日志如何記錄類似結(jié)構(gòu)體等復(fù)雜結(jié)構(gòu)類型的變量,畢竟日志對(duì)于問題調(diào)試至關(guān)重要。后面有機(jī)會(huì),可單獨(dú)談下這個(gè)主題。

最后,希望這篇文章能幫助你在打印調(diào)試 GO 結(jié)構(gòu)體等復(fù)雜結(jié)構(gòu)時(shí),不再迷茫。

以上就是Go打印結(jié)構(gòu)體提升代碼調(diào)試效率實(shí)例詳解的詳細(xì)內(nèi)容,更多關(guān)于Go打印結(jié)構(gòu)體的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 玩轉(zhuǎn)Go命令行工具Cobra

    玩轉(zhuǎn)Go命令行工具Cobra

    這篇文章主要介紹了玩轉(zhuǎn)Go命令行工具Cobra,本文介紹了Cobra的最基本也是最常用的使用部分,但是Cobra仍然有很多優(yōu)秀的操作值得我們學(xué)習(xí),需要的朋友可以參考下
    2022-08-08
  • go語(yǔ)言中讀取配置文件的方法總結(jié)

    go語(yǔ)言中讀取配置文件的方法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了go語(yǔ)言中讀取配置文件的幾個(gè)常見方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考下
    2023-08-08
  • 解決golang處理http response碰到的問題和需要注意的點(diǎn)

    解決golang處理http response碰到的問題和需要注意的點(diǎn)

    這篇文章主要介紹了解決golang處理http response碰到的問題和需要注意的點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2020-12-12
  • Go設(shè)計(jì)模式之狀態(tài)模式講解和代碼示例

    Go設(shè)計(jì)模式之狀態(tài)模式講解和代碼示例

    狀態(tài)是一種行為設(shè)計(jì)模式,?讓你能在一個(gè)對(duì)象的內(nèi)部狀態(tài)變化時(shí)改變其行為,該模式將與狀態(tài)相關(guān)的行為抽取到獨(dú)立的狀態(tài)類中,?讓原對(duì)象將工作委派給這些類的實(shí)例,?而不是自行進(jìn)行處理,本文將通過代碼示例給大家簡(jiǎn)單的介紹一下Go狀態(tài)模式
    2023-08-08
  • gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法

    gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法

    今天小編就為大家分享一篇gorm golang 并發(fā)連接數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2019-07-07
  • Golang使用lua腳本實(shí)現(xiàn)redis原子操作

    Golang使用lua腳本實(shí)現(xiàn)redis原子操作

    這篇文章主要介紹了Golang使用lua腳本實(shí)現(xiàn)redis原子操作,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的工作或?qū)W習(xí)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • golang與pgsql交互的實(shí)現(xiàn)

    golang與pgsql交互的實(shí)現(xiàn)

    本文主要介紹了golang與pgsql交互的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)

    GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)

    這篇文章主要為大家介紹了GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • 深入理解go?reflect反射慢的原因

    深入理解go?reflect反射慢的原因

    我們選擇?go?語(yǔ)言的一個(gè)重要原因是,它有非常高的性能。但是它反射的性能卻一直為人所詬病,本篇文章就來(lái)看看?go?反射的性能問題,感興趣的可以了解一下
    2023-01-01
  • Go語(yǔ)言中的格式化輸出占位符的用法詳解

    Go語(yǔ)言中的格式化輸出占位符的用法詳解

    在 Go 語(yǔ)言中,格式化輸出是一個(gè)非常常用的功能,特別是在處理字符串、數(shù)字和其他數(shù)據(jù)類型時(shí),本文將詳細(xì)介紹 Go 語(yǔ)言中常用的格式化占位符及其用法,希望對(duì)大家有所幫助
    2024-10-10

最新評(píng)論

桑日县| 万载县| 资中县| 开阳县| 连州市| 酉阳| 揭西县| 讷河市| 武城县| 信宜市| 贺兰县| 石林| 利津县| 贵阳市| 天峨县| 南雄市| 淮阳县| 宜宾市| 大荔县| 和顺县| 喀什市| 托克逊县| 孟连| 天津市| 临泽县| 且末县| 蓝山县| 南通市| 万全县| 曲水县| 荆州市| 额尔古纳市| 南昌市| 石河子市| 福安市| 丰宁| 普宁市| 民乐县| 安图县| 夹江县| 启东市|