V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
rix
V2EX  ›  Go 编程语言

Golang 結構體格式化打印工具

  •  
  •   rix · 2022-09-18 01:02:52 +08:00 · 1657 次点击
    这是一个创建于 558 天前的主题,其中的信息可能已经有所发展或是发生改变。

    很多時候我需要格式化打印輸出一些複雜的結構體,但是 Golang 自帶的 "%#v" 格式化不是很好讀,可定製性也不高,而且會把很多無關的私有值都打印出來,比如 time.Time

    因此我寫了個可以完全自定義,能方便地控制輸出格式,管理縮進,而且有簡單接口的結構體格式化模塊:

    gopkg.in/structformat.v0

    比如說你有這樣一個結構體定義:

    type Encryption struct {
    	Key        []byte
    	IV         []byte
    	Tag        []byte
    	Mode       string
    	Plaintext  string
    	Ciphertext []byte
    }
    

    你想格式化打印一份這樣的數據:

    enc := &Encryption{
        Key:  []byte{0xf8,0x0f,0xe1,0xef,0x1b,0x28,0x92,0xe8,0x12,0x0f,0x0d,0x75,0xa1,0xb0,0x67,0xce},
        IV:   []byte{0xdf,0x3a,0xb2,0x8c,0x39,0xb9,0xa4,0x52,0xce,0xfd,0xd2,0x9d,0x0a,0x02,0xd4,0x1d},
        Tag:  nil,
        Mode: "CTR",
        Plaintext: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et \n" +
            "dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex \n" +
            "ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu \n" +
            "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt \n" +
            "mollit anim id est laborum",
        Ciphertext: []byte{
            0xa3,0xb1,0x24,0x93,0x30,0x88,0x31,0xdc,0x3d,0x32,0xea,0x7b,0x06,0xbe,0x86,0xba,
            0x31,0xe7,0x72,0x5a,0x9c,0x72,0x9c,0xa4,0xea,0x4b,0xa1,0x5f,0x95,0x47,0xad,0xfc,
            0x5e,0x40,0x15,0x62,0x4e,0x35,0xe4,0xe5,0xfe,0x53,0x24,0x68,0x8a,0x77,0x0c,0xb7,
            0xf9,0x8c,0xac,0x4d,0xdf,0x66,0x64,0x86,0x5a,0xba,0xcc,0xeb,0x68,0x16,0x43,0xd2,
            0x84,0x37,0xaf,0x4b,0xe4,0x47,0x2a,0x84,0x1e,0x0a,0x1d,0xff,0x7d,0x76,0x00,0x47,
            0xe6,0x51,0x96,0xfa,0x19,0x96,0x1b,0xa9,0x04,0x42,0xcf,0xbc,0xc0,0x88,0x71,0x03,
            0xe7,0x49,0x4c,0x0e,0xf5,0x0f,0x1f,0x8c,0x69,0x3e,0x65,0x07,0x90,0x56,0xc7,0xa7,
            0xf1,0x95,0xd6,0xb2,0xbb,0xb8,0x3a,0x8e,0x66,0xbc,0x0a,0xbc,0x20,0x79,0xcc,0xa8,
            0xa8,0x65,0x23,0x5c,0xcf,0xe9,0x4d,0x76,0x6e,0x13,0xf0,0x64,0x13,0x5a,0x08,0xab,
            0xdf,0x90,0x83,0xce,0x01,0x5a,0x5e,0xaa,0x3d,0x0e,0x46,0x76,0x04,0x99,0xe0,0x16,
            0xb2,0x76,0xbe,0x28,0x4b,0xe2,0x12,0xf5,0xdc,0x60,0x5b,0x67,0xe6,0xaa,0x2c,0x70,
            0x49,0x7e,0x55,0x5c,0x1c,0x0a,0x60,0x36,0x69,0x97,0xf3,0x29,0x04,0x90,0x5f,0x60,
            0xd7,0x65,0x60,0xa8,0xc0,0x76,0xd1,0xe3,0x42,0xc1,0x0e,0x63,0xf9,0xe0,0x72,0xec,
            0x95,0x97,0x44,0x9c,0xea,0xf3,0x21,0x50,0x5b,0xa7,0x3e,0xde,0xe3,0x8c,0x7d,0xee,
            0xcc,0x64,0x74,0x77,0xb5,0xcc,0xdc,0x8e,0xe5,0x97,0x63,0x6b,0x7f,0x56,0x5c,0x5d,
            0x7f,0x6f,0x68,0x8b,0x5b,0xa6,0x37,0xe8,0xf5,0xb7,0xfc,0xf9,0x96,0xb6,0xc0,0xb7,
            0x17,0x3d,0xd1,0xfe,0x11,0x6d,0x39,0x39,0x37,0xbe,0x1f,0xe7,0x1f,0x13,0x99,0x9f,
            0xdb,0x2f,0xcc,0xc8,0x46,0x92,0x85,0x12,0xb0,0x41,0x0a,0x14,0xd1,0x8f,0x46,0xf4,
            0x5e,0xac,0x1c,0xf5,0xa7,0x40,0xe1,0xfe,0x51,0xc4,0xe0,0x1a,0x12,0x37,0xee,0x97,
            0x63,0x47,0x50,0x4c,0xfa,0x87,0x4b,0xc6,0xe7,0xda,0x31,0x4d,0x13,0x62,0x39,0xb2,
            0xfe,0x3a,0xb5,0x96,0x53,0x90,0xd5,0x06,0x8d,0x57,0x22,0x7f,0xc3,0xcd,0x75,0x99,
            0xff,0x45,0x51,0x10,0xbc,0x03,0x0b,0xb8,0x82,0x5d,0x77,0xd6,0xb5,0xa2,0x81,0x00,
            0xb4,0x78,0xe3,0xd3,0xcd,0xad,0x37,0xf3,0x76,0xf8,0x40,0x2d,0x5e,0xf1,0x17,0x01,
            0xd0,0x13,0x4b,0xbe,0x4c,0x7e,0x1d,0x67,0x54,0xd0,0xc6,0x19,0x7a,0x91,0x91,0xc7,
            0x5f,0xad,0xd5,0x99,0x4e,0x03,0x79,0x7e,0x86,0x9a,0x38,0xbf,0x12,0x1a,0xa0,0x6e,
            0x32,0xb2,0x41,0xda,0x11,0x4e,0xfc,0x3c,0x61,0x5a,0x02,0x95,0x2b,0x6b,0x15,0xc6,
            0xa9,0xcd,0xc2,0x56,0xc1,0xc3,0x2a,0x43,0xc6,0xbf,0xfc,0x0b,0x46,0xe8,0xc7,0xee,
            0xea,0x64,0x9b,0x08,0x42,0x6a,0x47,0x9d,0x4c,0xce,0xe7,0x95,
        },
    }
    

    利用這個模塊,你需要給這個結構體類型實現一個構建結構體打印樣式的接口。這個模塊提供了很多構建樣式的基本函數,通過拼接組合這些函數就能以聲明式的方式定義樣式了。這種模式非常類似現在格式化日誌的做法:

    func (enc *Encryption) StructFormat(w sfmt.Writer) (n int, err error) {
    	return sfmt.Struct(
    		`Encryption`,
    		sfmt.KV(`Key`, sfmt.HexBytes(enc.Key)),
    		sfmt.KV(`IV`, sfmt.HexBytes(enc.IV)),
    		sfmt.KV(`Tag`, sfmt.HexBytes(enc.Tag)),
    		sfmt.KV(`Mode`, sfmt.String(enc.Mode)),
    		sfmt.KV(`Plaintext`, sfmt.String(enc.Plaintext), sfmt.KVNewLine()),
    		sfmt.KV(`Ciphertext`, sfmt.Hexdump(enc.Ciphertext), sfmt.KVNewLine()),
    	).StructFormat(w)
    }
    

    然後在主程序中打印這個結構體只要這樣:

        sfmt.Format(os.Stdout, enc)
    

    最終的打印輸出如下:

    Encryption {
        Key: 0xf80fe1ef1b2892e8120f0d75a1b067ce
        IV: 0xdf3ab28c39b9a452cefdd29d0a02d41d
        Tag: ( EMPTY )
        Mode: CTR
        Plaintext:
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et 
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex 
            ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 
            fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt 
            mollit anim id est laborum
        Ciphertext:
            00000000  a3 b1 24 93 30 88 31 dc  3d 32 ea 7b 06 be 86 ba  |..$.0.1.=2.{....|
            00000010  31 e7 72 5a 9c 72 9c a4  ea 4b a1 5f 95 47 ad fc  |1.rZ.r...K._.G..|
            00000020  5e 40 15 62 4e 35 e4 e5  fe 53 24 68 8a 77 0c b7  |^@.bN5...S$h.w..|
            00000030  f9 8c ac 4d df 66 64 86  5a ba cc eb 68 16 43 d2  |...M.fd.Z...h.C.|
            00000040  84 37 af 4b e4 47 2a 84  1e 0a 1d ff 7d 76 00 47  |.7.K.G*.....}v.G|
            00000050  e6 51 96 fa 19 96 1b a9  04 42 cf bc c0 88 71 03  |.Q.......B....q.|
            00000060  e7 49 4c 0e f5 0f 1f 8c  69 3e 65 07 90 56 c7 a7  |.IL.....i>e..V..|
            00000070  f1 95 d6 b2 bb b8 3a 8e  66 bc 0a bc 20 79 cc a8  |......:.f... y..|
            00000080  a8 65 23 5c cf e9 4d 76  6e 13 f0 64 13 5a 08 ab  |.e#\..Mvn..d.Z..|
            00000090  df 90 83 ce 01 5a 5e aa  3d 0e 46 76 04 99 e0 16  |.....Z^.=.Fv....|
            000000a0  b2 76 be 28 4b e2 12 f5  dc 60 5b 67 e6 aa 2c 70  |.v.(K....`[g..,p|
            000000b0  49 7e 55 5c 1c 0a 60 36  69 97 f3 29 04 90 5f 60  |I~U\..`6i..).._`|
            000000c0  d7 65 60 a8 c0 76 d1 e3  42 c1 0e 63 f9 e0 72 ec  |.e`..v..B..c..r.|
            000000d0  95 97 44 9c ea f3 21 50  5b a7 3e de e3 8c 7d ee  |..D...!P[.>...}.|
            000000e0  cc 64 74 77 b5 cc dc 8e  e5 97 63 6b 7f 56 5c 5d  |.dtw......ck.V\]|
            000000f0  7f 6f 68 8b 5b a6 37 e8  f5 b7 fc f9 96 b6 c0 b7  |.oh.[.7.........|
            00000100  17 3d d1 fe 11 6d 39 39  37 be 1f e7 1f 13 99 9f  |.=...m997.......|
            00000110  db 2f cc c8 46 92 85 12  b0 41 0a 14 d1 8f 46 f4  |./..F....A....F.|
            00000120  5e ac 1c f5 a7 40 e1 fe  51 c4 e0 1a 12 37 ee 97  |^[email protected]..|
            00000130  63 47 50 4c fa 87 4b c6  e7 da 31 4d 13 62 39 b2  |cGPL..K...1M.b9.|
            00000140  fe 3a b5 96 53 90 d5 06  8d 57 22 7f c3 cd 75 99  |.:..S....W"...u.|
            00000150  ff 45 51 10 bc 03 0b b8  82 5d 77 d6 b5 a2 81 00  |.EQ......]w.....|
            00000160  b4 78 e3 d3 cd ad 37 f3  76 f8 40 2d 5e f1 17 01  |.x....7.v.@-^...|
            00000170  d0 13 4b be 4c 7e 1d 67  54 d0 c6 19 7a 91 91 c7  |..K.L~.gT...z...|
            00000180  5f ad d5 99 4e 03 79 7e  86 9a 38 bf 12 1a a0 6e  |_...N.y~..8....n|
            00000190  32 b2 41 da 11 4e fc 3c  61 5a 02 95 2b 6b 15 c6  |2.A..N.<aZ..+k..|
            000001a0  a9 cd c2 56 c1 c3 2a 43  c6 bf fc 0b 46 e8 c7 ee  |...V..*C....F...|
            000001b0  ea 64 9b 08 42 6a 47 9d  4c ce e7 95 
    }
    

    可以看到結構體內部的鍵值都被縮進處理了,增加了可讀性。如果有更多層級的結構體嵌套,縮進也會自動增加。二進制內容也可以有更豐富的輸出樣式比如 Hexdump 。模塊還提供了一個 Writer 實現可以方便地創造更多新的樣式構建函數。

    範例代碼: https://go.dev/play/p/bPjR5OdA_et

    6 条回复    2022-09-25 03:10:26 +08:00
    kkeep
        1
    kkeep  
       2022-09-18 02:09:25 +08:00 via Android
    有意思
    janxin
        2
    janxin  
       2022-09-18 20:23:07 +08:00
    可以实现 GoStringer 接口吧 https://pkg.go.dev/fmt#GoStringer
    rix
        3
    rix  
    OP
       2022-09-19 01:02:41 +08:00
    @janxin GoStringer 不帶縮進管理,雖然可以自己實現一個 helper 專門處理縮進。
    但是如果數據量大返回 String 會很慢而且耗內存。Writer 實現走流數據可以更好處理大量的輸出。
    Kisesy
        4
    Kisesy  
       2022-09-19 15:59:13 +08:00
    不错,可以自定义打印,我比较常用 github.com/kr/pretty 主要是地址比较好记
    rix
        5
    rix  
    OP
       2022-09-20 03:50:43 +08:00
    @Kisesy 我也是之前一直在用 github.com/kr/pretty 但是當需求稍微複雜一點就感覺到不夠用了
    someonedeng
        6
    someonedeng  
       2022-09-25 03:10:26 +08:00
    好东西,开发的时候比较方便
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1122 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 22:50 · PVG 06:50 · LAX 15:50 · JFK 18:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.