您好,登錄后才能下訂單哦!
功能:fmt包實現了類似C語言printf和scanf的格式化I/O。格式化動作('verb')源自C語言但更簡單。
Printf 函數有超過10個各種轉義字符,Go 程序員稱為 verb。下表不完整,但是它說明了很多可用的功能:
verb | 描述 |
---|---|
%d | 十進制數 |
%x, %o, %b | 十六進制,八進制,二進制數 |
%f, %g, %e | 浮點數 |
%t | 布爾型 |
%c | 字符(Unicode碼點) |
%s | 字符串 |
%q | 帶引號字符串或者字符 |
%v | 內置格式的任何值 |
%T | 任何值的類型 |
%% | 百分號本身 |
以ln結尾的,比如fmt.Println,使用%v的方式來格式化參數,并且在最后追加換行符。
上面的表格比較概括,而且也不全,對于不同的類型,還有不同的細節。所有的說明都在fmt包的doc.go文檔里有詳細的說明:http://docscn.studygolang.com/src/fmt/doc.go
普通占位符:
占位符 | 說明 | 舉例 | 輸出 |
---|---|---|---|
%v | 相應值的默認格式。 | Printf("%v", people) | {zhangsan} |
%+v | 打印結構體時,會添加字段名 | Printf("%+v", people) | {Name:zhangsan} |
%#v | 相應值的Go語法表示 | Printf("%#v", people) | main.Human{Name:"zhangsan"} |
%T | 相應值的類型的Go語法表示 | Printf("%T", people) | main.Human |
%% | 字面上的百分號,并非值的占位符 | Printf("%%") | % |
布爾占位符:
占位符 | 說明 | 舉例 | 輸出 |
---|---|---|---|
%t | true 或 false。 | Printf("%t", true) | true |
整數占位符:
占位符 | 說明 | 舉例 | 輸出 |
---|---|---|---|
%b | 二進制表示 | Printf("%b", 5) | 101 |
%c | 相應Unicode碼點所表示的字符 | Printf("%c", 0x4E2D) | 中 |
%d | 十進制表示 | Printf("%d", 0x12) | 18 |
%o | 八進制表示 | Printf("%d", 10) | 12 |
%q | 單引號圍繞的字符字面值,由Go語法安全地轉義 | Printf("%q", 0x4E2D) | '中' |
%x | 十六進制表示,字母形式為小寫 a-f | Printf("%x", 13) | d |
%X | 十六進制表示,字母形式為大寫 A-F | Printf("%x", 13) | D |
%U | Unicode格式,相當于 "%04X" 加上前導 "U+" | Printf("%U", 0x4E2D) | U+4E2D |
浮點數和復數:
占位符 | 說明 | 舉例 | 輸出 |
---|---|---|---|
%b | 無小數部分的,指數為二的冪的科學計數法,與 strconv.FormatFloat 的 'b' 轉換格式一致。例如 -123456p-78 | ||
%e | 科學計數法,例如 -1234.456e+78 | Printf("%e", 10.2) | 1.020000e+01 |
%E | 科學計數法,例如 -1234.456E+78 | Printf("%e", 10.2) | 1.020000E+01 |
%f | 有小數點而無指數,例如 123.456 | Printf("%f", 10.2) | 10.200000 |
%g | 根據情況選擇 %e 或 %f 以產生更緊湊的(無末尾的0)輸出 | Printf("%g", 10.20) | 10.2 |
%G | 根據情況選擇 %E 或 %f 以產生更緊湊的(無末尾的0)輸出 | Printf("%G", 10.20+2i) | (10.2+2i) |
指針:
占位符 | 說明 | 舉例 | 輸出 |
---|---|---|---|
%p | 十六進制表示,前綴 0x | Printf("%p", &people) | 0x4f57f0 |
其他標記(副詞):
fmt.Printf("%q %+[1]q\n", "中文") // "中文" "\u4e2d\u6587"
副詞#的備用格式:
八進制、十六進制,默認沒有前導,使用#后會添加前導符號"0"、"0x"、"0X",防止產生歧義:
fmt.Printf("%o %#[1]o\n", 123) // 173 0173
fmt.Printf("%x %#[1]x %#[1]X\n", 123) // 7b 0x7b 0X7B
指針默認有前導,備用格式就是就掉前導:
var s string
fmt.Printf("%p %#[1]p\n", &s) // 0xc00004c240 c00004c240
對于字符串,%#q有些情況下會輸出反引號圍繞的字符串,不過測試下來不總是這樣:
fmt.Printf("%q, %#[1]q\n", "ab\tcd") // "ab\tcd", `ab cd`
fmt.Printf("%q, %#[1]q\n", "ab\ncd") // "ab\ncd", "ab\ncd"
對于Unicode,打印出字符的編碼后還會打印該字符:
fmt.Printf("%U, %#[1]U", '中') // U+4E2D, U+4E2D '中'
下面是一些可以通過合理的構造格式化字符串來打到最佳的顯示效果的示例。
可能有些示例之間會有點重復。還有一些示例因為太簡單,感覺上一節已經講過了。不過上一節的介紹偏理論,而這里的內容更注重實際使用。
一、%后的副詞[1]告知Printf重復使用第一個操作數。
二、%o、%x、%X之前的副詞#告知Printf輸出相應的前綴 0、0x、0X。
func main() {
o := 0666
fmt.Printf("%d %[1]o %#[1]o\n", o) // 438 666 0666
x := int64(0xdeadbeef)
fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x) // 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
}
使用%x可以輸出字節序列的UTF-8編碼,還可以加上空格也就是% x,這樣每個字符還能隔開,在對字節序列進行輸出的時候特別有用:
fmt.Printf("%x\n", "abcdefg") // "61626364656667"
fmt.Printf("% x\n", "abcdefg") // "61 62 63 64 65 66 67"
fmt.Printf("% #x\n", "abcdefg") // "0x61 0x62 0x63 0x64 0x65 0x66 0x67"
對于Unicode字符,還是按照字節處理的,如若要輸出Unicode碼點,需要先轉成[]rune類型:
fmt.Printf("% x\n", "世界") // "e4 b8 96 e7 95 8c"
fmt.Printf("%x\n", []rune("世界")) // "[4e16 754c]"
通過指定相同的寬度,可以做到右對齊的效果:
func main() {
fmt.Printf("%4d\n", 1)
fmt.Printf("%4d\n", 10)
fmt.Printf("%4d\n", 100)
fmt.Printf("%4d\n", 1000)
fmt.Printf("%4d\n", 10000) // 這個會超出寬度
}
/* 輸出結果
$ go run main.go
1
10
100
1000
10000
*/
這里的頁面似乎看不出對齊的效果,可能是空格的寬度不太一樣。下面使用0填充的對齊效果就有了
如果寬度不夠,輸出時也不會丟失信息,而是把信息全部輸出,不受寬度的限制。
默認使用空格填充,也可以指定填充的內容,比如使用0填充,在輸出二進制數的時候非常有用:
func main() {
fmt.Printf("%04b\n", 1)
fmt.Printf("%04b\n", 2)
fmt.Printf("%04b\n", 3)
fmt.Printf("%04b\n", 4)
fmt.Printf("%04b\n", 666) // 這個會超出寬度
}
/* 輸出結果
$ go run main.go
0001
0010
0011
0100
1010011010
*/
操作數字的時候,寬度為該數值占用區域的最小寬度;精度為小數點之后的位數。
對于 %g 和 %G 精度是所有數字的總和,而用 %f 打印出來同樣是小數,精度是小數點后面的位數。比如:123.45,%.4g 是 "123.5" 而 %.2f 是 "123.45"。
const n float64 = 123.45
fmt.Printf("%.4g %.2[1]f", n) // g:123.5 f:123.45
打印結構體的時候,使用副詞#或者+可以使結構化符號%v以類似Go語法的方式輸出對象,這個方法里面包含了成員變量的名字:
package main
import "fmt"
// 這可以表示一個坐標
type Point struct {
X, Y int
}
// 坐標加上半徑就是一個圓
type Circle struct {
Point
Radius int
}
// 圓加上輻條數,這表示一個輪子
type Wheel struct {
Circle
Spokes int
}
var w Wheel
func main() {
w = Wheel{Circle{Point{8, 8}, 5}, 20}
w = Wheel{
Circle: Circle{
Point: Point{X: 8, Y: 8},
Radius: 5,
},
Spokes: 20,
}
fmt.Printf("%v\n", w)
fmt.Printf("%#v\n", w)
}
/* 執行結果
PS H:\Go\src\gopl\ch5\embed> go run main.go
{{{8 8} 5} 20}
main.Wheel{Circle:main.Circle{Point:main.Point{X:8, Y:8}, Radius:5}, Spokes:20}
PS H:\Go\src\gopl\ch5\embed>
*/
寬度和精度對于字符串輸出同樣有效:
這樣就可以在輸出字符串的時候做到左對齊和右對齊,輸出類似表格的樣式:
package main
import "fmt"
type message struct {
Title string
text string
}
func main() {
list := []message{
{"fmt", "Package fmt implements formatted I/O with functions analogous to C's printf and scanf. The format 'verbs' are derived from C's but are simpler. "},
{"bytes", "Package bytes implements functions for the manipulation of byte slices. It is analogous to the facilities of the strings package. "},
{"time", "Package time provides functionality for measuring and displaying time. "},
{"net/http", "Package http provides HTTP client and server implementations. "},
}
_ = list
for _, msg := range list {
fmt.Printf("%9.9s %.99s\n", msg.Title, msg.text)
}
}
/* 輸出效果
PS H:\Go\src\gopl\output> go run main.go
fmt Package fmt implements formatted I/O with functions analogous to C's printf and scanf. The format '
bytes Package bytes implements functions for the manipulation of byte slices. It is analogous to the faci
time Package time provides functionality for measuring and displaying time.
net/http Package http provides HTTP client and server implementations.
PS H:\Go\src\gopl\output>
*/
不過對于中文輸出就不會那么漂亮了,因為這里填充的空格是半角的空格。
左對齊
其實只要用上制表符\t就能對齊了,不過如果字符串的長度相差比較大,也無法對齊,并且間隔是自動的。下面是另一種精確的實現方法。
默認是在前面進行填充,似乎也沒有在后面填充的格式。不過可以用下面的方法輸出左對齊的效果:
fmt.Printf("%s%*s %.99s\n", msg.Title, 10-len(msg.Title), "", msg.text)
這里的%*s
是在s對應的內容輸出前,動態的填充由變量指定的前導空格。由于這里s對應的是空字符串,所以就相當于是動態添加空格,而空格的數量是可以根據前一個變量的長度計算后動態變化的。這是下一節的內容,不過這里正好先展示下效果。
還有一種控制寬度的方法,使用*號,這種方式輸出的寬度是由之后的變量決定的所以是可變的:
func main() {
for i := 0; i < 3; i++ {
fmt.Printf("%*d\n", i*4, i)
}
}
/* 執行結果
PS H:\Go\src\gopl\output> go run main.go
0
1
2
PS H:\Go\src\gopl\output> go run main.go
*/
輸出的寬度由第一個變量控制,而輸出的內容是第二個變量。
這里加了個0,這樣就用0來代替原來的空格來填補寬度了,下面顯示的效果更加直觀:
func main() {
for i := 0; i < 3; i++ {
fmt.Printf("%0*d\n", i*4, i)
}
}
/* 執行結果
PS H:\Go\src\gopl\output> go run main.go
0
0001
00000002
PS H:\Go\src\gopl\output>
*/
由于寬度是包含字符串本身的,而縮進效果是不包括字符串的。所以可是使用空字符串作為第二個參數,把要輸出的內容放在后面。
推薦這么用,fmt.Printf("%*s<%s>\n", n*2, "", "div")
第一個變量控制寬度,第二個是輸出的字符串,這里是空字符串,這里就是兩格縮進的效果。把要輸出的內容放在后面,這里的第三個變量。
這種顯示效果在處理代碼和html標簽的時候特別好,下面的程序先通過Get請求獲取一個頁面,然后解析頁面中的文檔樹,并輸出有縮進效果的樹結構:
package main
import (
"fmt"
"net/http"
"os"
"golang.org/x/net/html"
)
func main() {
for _, url := range os.Args[1:] {
outline(url)
}
}
func outline(url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
doc, err := html.Parse(resp.Body)
if err != nil {
return err
}
forEachNode(doc, startElement, endElement)
return nil
}
// 調用 pre(x) 和 post(x) 遍歷以n為根的樹中的每個節點x
// 兩個函數都是可選的
// pre 在子節點被訪問前調用(前序調用)
// post 在訪問后調用(后續調用)
func forEachNode(n *html.Node, pre, post func(n *html.Node)) {
if pre != nil {
pre(n)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
forEachNode(c, pre, post)
}
if post != nil {
post(n)
}
}
var depth int
func startElement(n *html.Node) {
if n.Type == html.ElementNode {
fmt.Printf("%*s<%s>\n", depth*2, "", n.Data)
depth++
}
}
func endElement(n *html.Node) {
if n.Type == html.ElementNode {
depth--
fmt.Printf("%*s</%s>\n", depth*2, "", n.Data)
}
}
/* 執行結果
PS H:\Go\src\gopl\ch6\outline2> go run main.go http://baidu.com
<html>
<head>
<meta>
</meta>
</head>
<body>
</body>
</html>
PS H:\Go\src\gopl\ch6\outline2>
*/
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。