天天看點

換個語言學一下 Golang(14) ——fmt包

Print() 函數将參數清單 a 中的各個參數轉換為字元串并寫入到标準輸出中。

非字元串參數之間會添加空格,傳回寫入的位元組數。

func Print(a ...interface{}) (n int, err error)      

Println() 函數功能類似 Print,隻不過最後會添加一個換行符。

所有參數之間會添加空格,傳回寫入的位元組數。

func Println(a ...interface{}) (n int, err error)      

Printf() 函數将參數清單 a 填寫到格式字元串 format 的占位符中。

填寫後的結果寫入到标準輸出中,傳回寫入的位元組數。

func Printf(format string, a ...interface{}) (n int, err error)      
以下三個函數功能同上面三個函數,隻不過将轉換結果寫入到 w 中。      
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)      

以下三個函數功能同上面三個函數,隻不過将轉換結果以字元串形式傳回。

func Sprint(a ...interface{}) string
func Sprintln(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string      

以下函數功能同 Sprintf() 函數,隻不過結果字元串被包裝成了 error 類型。

func Errorf(format string, a ...interface{}) error      

執行個體:

func main() {
    fmt.Print("a", "b", 1, 2, 3, "c", "d", "\n")
    fmt.Println("a", "b", 1, 2, 3, "c", "d")
    fmt.Printf("ab %d %d %d cd\n", 1, 2, 3)
    // ab1 2 3cd
    // a b 1 2 3 c d
    // ab 1 2 3 cd

    if err := percent(30, 70, 90, 160); err != nil {
        fmt.Println(err)
    }
    // 30%
    // 70%
    // 90%
    // 數值 160 超出範圍(100)
}      
func percent(i ...int) error {
    for _, n := range i {
        if n > 100 {
            return fmt.Errorf("數值 %d 超出範圍(100)", n)
        }
        fmt.Print(n, "%\n")
    }
    return nil
}      

Formatter 由自定義類型實作,用于實作該類型的自定義格式化過程。

當格式化器需要格式化該類型的變量時,會調用其 Format 方法。

type Formatter interface {
    // f 用于擷取占位符的旗标、寬度、精度等資訊,也用于輸出格式化的結果
    // c 是占位符中的動詞
    Format(f State, c rune)
}      

由格式化器(Print 之類的函數)實作,用于給自定義格式化過程提供資訊:

type State interface {
    // Formatter 通過 Write 方法将格式化結果寫入格式化器中,以便輸出。
    Write(b []byte) (ret int, err error)
    // Formatter 通過 Width 方法擷取占位符中的寬度資訊及其是否被設定。
    Width() (wid int, ok bool)
    // Formatter 通過 Precision 方法擷取占位符中的精度資訊及其是否被設定。
    Precision() (prec int, ok bool)
    // Formatter 通過 Flag 方法擷取占位符中的旗标[+- 0#]是否被設定。
    Flag(c int) bool
}      

Stringer 由自定義類型實作,用于實作該類型的自定義格式化過程。

當格式化器需要輸出該類型的字元串格式時就會調用其 String 方法。

type Stringer interface {
    String() string
}      

當格式化器需要輸出該類型的 Go 文法字元串(%#v)時就會調用其 String 方法。

type GoStringer interface {
    GoString() string
}      
type Ustr string

func (us Ustr) String() string {
    return strings.ToUpper(string(us))
}

func (us Ustr) GoString() string {
    return `"` + strings.ToUpper(string(us)) + `"`
}

func (u Ustr) Format(f fmt.State, c rune) {
    write := func(s string) {
        f.Write([]byte(s))
    }
    switch c {
    case 'm', 'M':
        write("旗标:[")
        for s := "+- 0#"; len(s) > 0; s = s[1:] {
            if f.Flag(int(s[0])) {
                write(s[:1])
            }
        }
        write("]")
        if v, ok := f.Width(); ok {
            write(" | 寬度:" + strconv.FormatInt(int64(v), 10))
        }
        if v, ok := f.Precision(); ok {
            write(" | 精度:" + strconv.FormatInt(int64(v), 10))
        }
    case 's', 'v': // 如果使用 Format 函數,則必須自己處理所有格式,包括 %#v
        if c == 'v' && f.Flag('#') {
            write(u.GoString())
        } else {
            write(u.String())
        }
    default: // 如果使用 Format 函數,則必須自己處理預設輸出
        write("無效格式:" + string(c))
    }
}

func main() {
    u := Ustr("Hello World!")
    // "-" 标記和 "0" 标記不能同時存在
    fmt.Printf("%-+ 0#8.5m\n", u) // 旗标:[+- #] | 寬度:8 | 精度:5
    fmt.Printf("%+ 0#8.5M\n", u)  // 旗标:[+ 0#] | 寬度:8 | 精度:5
    fmt.Println(u)                // HELLO WORLD!
    fmt.Printf("%s\n", u)         // HELLO WORLD!
    fmt.Printf("%#v\n", u)        // "HELLO WORLD!"
    fmt.Printf("%d\n", u)         // 無效格式:d
}      

Scan 從标準輸入中讀取資料,并将資料用空白分割并解析後存入 a 提供的變量中(換行符會被當作空白處理),變量必須以指針傳入。

當讀到 EOF 或所有變量都填寫完畢則停止掃描。

傳回成功解析的參數數量。

func Scan(a ...interface{}) (n int, err error)      
Scanln 和 Scan 類似,隻不過遇到換行符就停止掃描。      
func Scanln(a ...interface{}) (n int, err error)      
Scanf 從标準輸入中讀取資料,并根據格式字元串 format 對資料進行解析,将解析結果存入參數 a 所提供的變量中,變量必須以指針傳入。      

輸入端的換行符必須和 format 中的換行符相對應(如果格式字元串中有換行符,則輸入端必須輸入相應的換行符)。

占位符 %c 總是比對下一個字元,包括空白,比如空格符、制表符、換行符。

func Scanf(format string, a ...interface{}) (n int, err error)      
以下三個函數功能同上面三個函數,隻不過從 r 中讀取資料。      
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)      

以下三個函數功能同上面三個函數,隻不過從 str 中讀取資料。

func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)      

執行個體:

// 對于 Scan 而言,回車視為空白
func main() {
    a, b, c := "", 0, false
    fmt.Scan(&a, &b, &c)
    fmt.Println(a, b, c)
    // 在終端執行後,輸入 abc 1 回車 true 回車
    // 結果 abc 1 true
}

// 對于 Scanln 而言,回車結束掃描
func main() {
    a, b, c := "", 0, false
    fmt.Scanln(&a, &b, &c)
    fmt.Println(a, b, c)
    // 在終端執行後,輸入 abc 1 true 回車
    // 結果 abc 1 true
}

// 格式字元串可以指定寬度
func main() {
    a, b, c := "", 0, false
    fmt.Scanf("%4s%d%t", &a, &b, &c)
    fmt.Println(a, b, c)
    // 在終端執行後,輸入 1234567true 回車
    // 結果 1234 567 true
}      

Scanner 由自定義類型實作,用于實作該類型的自定義掃描過程。

當掃描器需要解析該類型的資料時,會調用其 Scan 方法。

type Scanner interface {
    // state 用于擷取占位符中的寬度資訊,也用于從掃描器中讀取資料進行解析。
    // verb 是占位符中的動詞
    Scan(state ScanState, verb rune) error
}      

由掃描器(Scan 之類的函數)實作,用于給自定義掃描過程提供資料和資訊。

type ScanState interface {
    // ReadRune 從掃描器中讀取一個字元,如果用在 Scanln 類的掃描器中,
    // 則該方法會在讀到第一個換行符之後或讀到指定寬度之後傳回 EOF。
    // 傳回“讀取的字元”和“字元編碼所占用的位元組數”
    ReadRune() (r rune, size int, err error)
    // UnreadRune 撤消最後一次的 ReadRune 操作,
    // 使下次的 ReadRune 操作得到與前一次 ReadRune 相同的結果。
    UnreadRune() error
    // SkipSpace 為 Scan 方法提供跳過開頭空白的能力。
    // 根據掃描器的不同(Scan 或 Scanln)決定是否跳過換行符。
    SkipSpace()
    // Token 用于從掃描器中讀取符合要求的字元串,
    // Token 從掃描器中讀取連續的符合 f(c) 的字元 c,準備解析。
    // 如果 f 為 nil,則使用 !unicode.IsSpace(c) 代替 f(c)。
    // skipSpace:是否跳過開頭的連續空白。傳回讀取到的資料。
    // 注意:token 指向共享的資料,下次的 Token 操作可能會覆寫本次的結果。
    Token(skipSpace bool, f func(rune) bool) (token []byte, err error)
    // Width 傳回占位符中的寬度值以及寬度值是否被設定
    Width() (wid int, ok bool)
    // 因為上面實作了 ReadRune 方法,是以 Read 方法永遠不應該被調用。
    // 一個好的 ScanState 應該讓 Read 直接傳回相應的錯誤資訊。
    Read(buf []byte) (n int, err error)
}      
type Ustr string

func (u *Ustr) Scan(state fmt.ScanState, verb rune) (err error) {
    var s []byte
    switch verb {
    case 'S':
        s, err = state.Token(true, func(c rune) bool { return 'A' <= c && c <= 'Z' })
        if err != nil {
            return
        }
    case 's', 'v':
        s, err = state.Token(true, func(c rune) bool { return 'a' <= c && c <= 'z' })
        if err != nil {
            return
        }
    default:
        return fmt.Errorf("無效格式:%c", verb)
    }
    *u = Ustr(s)
    return nil
}

func main() {
    var a, b, c, d, e Ustr
    n, err := fmt.Scanf("%3S%S%3s%2v%x", &a, &b, &c, &d, &e)
    fmt.Println(a, b, c, d, e)
    fmt.Println(n, err)
    // 在終端執行後,輸入 ABCDEFGabcdefg 回車
    // 結果:
    // ABC DEFG abc de
    // 4 無效格式:x
}      

繼續閱讀