很多時候我需要格式化打印輸出一些複雜的結構體,但是 Golang 自帶的 "%#v"
格式化不是很好讀,可定製性也不高,而且會把很多無關的私有值都打印出來,比如 time.Time
。
因此我寫了個可以完全自定義,能方便地控制輸出格式,管理縮進,而且有簡單接口的結構體格式化模塊:
比如說你有這樣一個結構體定義:
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 實現可以方便地創造更多新的樣式構建函數。
1
kkeep 2022-09-18 02:09:25 +08:00 via Android
有意思
|
2
janxin 2022-09-18 20:23:07 +08:00
可以实现 GoStringer 接口吧 https://pkg.go.dev/fmt#GoStringer
|
3
rix OP @janxin GoStringer 不帶縮進管理,雖然可以自己實現一個 helper 專門處理縮進。
但是如果數據量大返回 String 會很慢而且耗內存。Writer 實現走流數據可以更好處理大量的輸出。 |
4
Kisesy 2022-09-19 15:59:13 +08:00
不错,可以自定义打印,我比较常用 github.com/kr/pretty 主要是地址比较好记
|
5
rix OP @Kisesy 我也是之前一直在用 github.com/kr/pretty 但是當需求稍微複雜一點就感覺到不夠用了
|
6
someonedeng 2022-09-25 03:10:26 +08:00
好东西,开发的时候比较方便
|