// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // go/src/fmt/scan.go // version 1.7 // 格式化輸入輸出的用法請參考:http://www.cnblogs.com/golove/p/3284304.html package fmt import ( "errors" "io" "math" "os" "reflect" "strconv" "sync" "unicode/utf8" ) // ScanState 將掃描器的狀態報告給自定義類型的 Scan 方法。 type ScanState interface { // ReadRune 從輸入端讀取一個字符,若是用在 Scanln 類的掃描器中, // 則該方法會在讀到第一個換行符以後或讀到指定寬度以後返回 EOF。 // r :讀取的字符 // size:字符所佔用的字節數 // err :遇到的錯誤信息 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:是否跳過輸入端開頭的連續空白(經過 SkipSpace 方法)。 // token :存放讀取到的數據。 // err :遇到的錯誤信息。 // 注意:token 指向共享的數據,下次的 Token 操做可能會覆蓋本次的結果。 Token(skipSpace bool, f func(rune) bool) (token []byte, err error) // Width 返回佔位符中指定的寬度值(寬度值是字符個數,不是字節個數)。 // wid:獲取到的寬度值 // ok :是否指定了寬度值 Width() (wid int, ok bool) // 由於上面實現了 ReadRune 方法,因此 Read 方法永遠不該該被 Scan 方法調用。 // 一個好的 ScanState 應該讓 Read 直接返回相應的錯誤信息。 Read(buf []byte) (n int, err error) } // Scanner 用於讓自定義類型實現本身的掃描過程。 // Scan 方法會從輸入端讀取數據並將處理結果存入接收端,接收端必須是有效的指針。 // Scan 方法會被掃描器調用,只要對應的 arg 實現了該方法。 type Scanner interface { Scan(state ScanState, verb rune) error } // Scan 從標準輸入中讀取字符串(以空白分隔的值的序列)並解析爲具體的值, // 存入參數 a 所提供的變量中(參數 a 必須提供變量的地址)。換行視爲空白。 // 當讀到 EOF 或全部變量都填寫完畢則中止掃描。 // n :成功解析的參數數量 // err:解析過程當中遇到的錯誤信息 func Scan(a ...interface{}) (n int, err error) { return Fscan(os.Stdin, a...) } // Scanln 和 Scan 相似,只不過遇到換行符就中止掃描。 func Scanln(a ...interface{}) (n int, err error) { return Fscanln(os.Stdin, a...) } // Scanf 從標準輸入中讀取字符串,並根據格式字符串 format 對讀取的數據進行解析, // 存入參數 a 所提供的變量中(參數 a 必須提供變量的地址)。 // 輸入端的換行符必須和格式字符串中的換行符相對應(若是格式字符串中有換行符,則 // 輸入端必須輸入相應的換行符)。 // 佔位符 %c 老是匹配下一個字符,包括空白,好比空格符、製表符、換行符。 // n :成功解析的參數數量 // err:解析過程當中遇到的錯誤信息 func Scanf(format string, a ...interface{}) (n int, err error) { return Fscanf(os.Stdin, format, a...) } // 實現了 Reader 接口的字符串類型 type stringReader string func (r *stringReader) Read(b []byte) (n int, err error) { n = copy(b, *r) *r = (*r)[n:] if n == 0 { err = io.EOF } return } // Sscan 和 Scan 相似,只不過從 str 中讀取數據。 func Sscan(str string, a ...interface{}) (n int, err error) { return Fscan((*stringReader)(&str), a...) } // Sscanln 和 Scanln 相似,只不過從 str 中讀取數據。 func Sscanln(str string, a ...interface{}) (n int, err error) { return Fscanln((*stringReader)(&str), a...) } // Sscanf 和 Scanf 相似,只不過從 str 中讀取數據。 func Sscanf(str string, format string, a ...interface{}) (n int, err error) { return Fscanf((*stringReader)(&str), format, a...) } // Fscan 和 Scan 相似,只不過從 r 中讀取數據。 func Fscan(r io.Reader, a ...interface{}) (n int, err error) { s, old := newScanState(r, true, false) // 建立掃描器 n, err = s.doScan(a) // 開始掃描 s.free(old) // 回收掃描器 return } // Fscanln 和 Fcanln 相似,只不過從 r 中讀取數據。 func Fscanln(r io.Reader, a ...interface{}) (n int, err error) { s, old := newScanState(r, false, true) // 建立掃描器 n, err = s.doScan(a) // 開始掃描 s.free(old) // 回收掃描器 return } // Fscanf 和 Scanf 相似,只不過從 r 中讀取數據。 func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) { s, old := newScanState(r, false, false) // 建立掃描器 n, err = s.doScanf(format, a) // 開始掃描 s.free(old) // 回收掃描器 return } // scanError 聲明本地錯誤類型,用於 recover 時辨別 panic 是否由本地代碼產生的。 type scanError struct { err error } // 本地代碼用 -1 表示遇到 EOF const eof = -1 // ss 是掃描器,整個掃描過程都是由它完成的。 // 它從 rs 中讀取數據並進行解析。 type ss struct { rs io.RuneScanner // 輸入端 buf buffer // 緩衝區 count int // 已讀取的字符數 atEOF bool // 是否讀到 EOF ssave // 一些須要常常復位的字段 } // ssave 是 ss 的一部分,存儲一些須要常常復位的字段 type ssave struct { validSave bool // 平時用不上,遞歸時使用 nlIsEnd bool // 是否在換行符以後中止讀取 nlIsSpace bool // 是否將換行符視爲空白 argLimit int // 已讀的字符數不能超過 argLimit(argLimit <= limit) limit int // 已讀的字符數不能超過 limit(好像就當作常量在使用,用於復位 argLimit) maxWid int // 存儲佔位符中指定的寬度值 } // 實現 ScanState 接口 // Read 方法僅用於 ScanState 以知足 io.Reader 接口。 // 在內部永遠不會調用它,因此沒有必要讓它有任何動做。 func (s *ss) Read(buf []byte) (n int, err error) { return 0, errors.New("ScanState's Read should not be called. Use ReadRune") } // 實現 ScanState 接口 func (s *ss) ReadRune() (r rune, size int, err error) { // 讀到 EOF 或超出讀取限制,則返回 0 0 nil if s.atEOF || s.count >= s.argLimit { err = io.EOF return } r, size, err = s.rs.ReadRune() if err == nil { s.count++ // 統計被讀出的字符數 if s.nlIsEnd && r == '\n' { s.atEOF = true // 拒絕再次 ReadRune } } else if err == io.EOF { s.atEOF = true // 拒絕再次 ReadRune } return } // 實現 ScanState 接口 func (s *ss) Width() (wid int, ok bool) { if s.maxWid == hugeWid { // hugeWid 是常量 1 << 30 return 0, false } return s.maxWid, true } // 讀取一個字符,若是遇到 EOF 則返回 eof(即 -1) // 若是遇到其它錯誤,則停止整個掃描過程,返回 err。 func (s *ss) getRune() (r rune) { r, _, err := s.ReadRune() if err != nil { if err == io.EOF { return eof } s.error(err) } return } // 功能同 getRune,只不過遇到 EOF 也停止整個掃描過程,返回 err。 func (s *ss) mustReadRune() (r rune) { r = s.getRune() if r == eof { s.error(io.ErrUnexpectedEOF) } return } // 實現 ScanState 接口 func (s *ss) UnreadRune() error { s.rs.UnreadRune() s.atEOF = false // 容許再次 ReadRune s.count-- // 統計被讀出的字符數 return nil // UnreadRune 能夠反覆調用,不返回錯誤信息。 } // 將錯誤信息轉換爲 panic。 // 用於配合 recover 快速結束函數調用鏈,避免過多的返回值判斷。 // 相似於 break label 的用法。 func (s *ss) error(err error) { panic(scanError{err}) } // 做用同上面的 error 方法 func (s *ss) errorString(err string) { panic(scanError{errors.New(err)}) } // 實現 ScanState 接口 func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) { // 遇到本地錯誤則僅僅返回 err 信息。 // 遇到其它錯誤則 panic。 defer func() { if e := recover(); e != nil { if se, ok := e.(scanError); ok { err = se.err } else { panic(e) } } }() // 肯定審查函數 if f == nil { f = notSpace } // 準備緩衝區給 s.token 用 s.buf = s.buf[:0] tok = s.token(skipSpace, f) return } // space 是 unicode.White_Space 的拷貝,避免包的深度依賴。 // 這些都是空白字符的 Unicode 碼點範圍 var space = [][2]uint16{ {0x0009, 0x000d}, {0x0020, 0x0020}, {0x0085, 0x0085}, {0x00a0, 0x00a0}, {0x1680, 0x1680}, {0x2000, 0x200a}, {0x2028, 0x2029}, {0x202f, 0x202f}, {0x205f, 0x205f}, {0x3000, 0x3000}, } // 判斷 r 是否爲空白字符 func isSpace(r rune) bool { // 空白字符的碼點不會超過 2 個字節 if r >= 1<<16 { return false } // 開始判斷 rx := uint16(r) for _, rng := range space { if rx < rng[0] { return false } if rx <= rng[1] { return true } } return false } // notSpace 是 Token 中的默認審查函數。 func notSpace(r rune) bool { return !isSpace(r) } // 實現 ScanState 接口 func (s *ss) SkipSpace() { s.skipSpace(false) } // readRune 用於將 io.Reader 包裝成 io.RuneScanner type readRune struct { reader io.Reader // 被包裝的 io.Reader buf [utf8.UTFMax]byte // 僅在 ReadRune 方法中使用 pending int // pendBuf 中存放的字節數,遇到無效 UTF8 編碼時使用。 pendBuf [utf8.UTFMax]byte // 存放讀取的無效 UTF-8 編碼,一次處理不完,留着下次處理 peekRune rune // 用於 UnreadRune 存放撤銷的字符。 } // readByte 讀取一個字節 // 它多是上次 ReadRune 時未處理完的不完整 UTF8 編碼。 func (r *readRune) readByte() (b byte, err error) { // 若是以前的 ReadRune 有未處理完的字節,則從新讀出這些字節。 if r.pending > 0 { // 讀出一個字節 b = r.pendBuf[0] // 剩下的字節向前移動一格 copy(r.pendBuf[0:], r.pendBuf[1:]) r.pending-- return } // 若是沒有未處理的字節,則從輸入端讀出一個字節 n, err := io.ReadFull(r.reader, r.pendBuf[:1]) if n != 1 { return 0, err } return r.pendBuf[0], err } // 實現 io.RuneScanner 接口 func (r *readRune) ReadRune() (rr rune, size int, err error) { // 以前 UnreadRune 撤銷的字符,存在 peekRune 中,有則直接取出。 if r.peekRune >= 0 { rr = r.peekRune // 將 peekRune 取反爲負數,表示容許 UnreadRune 執行撤銷操做 r.peekRune = ^r.peekRune size = utf8.RuneLen(rr) return } // 沒有撤銷的字符,則從輸入端讀取一個 r.buf[0], err = r.readByte() if err != nil { return } // 若是讀出的是一個單字節字符,則讀取完畢。 if r.buf[0] < utf8.RuneSelf { rr = rune(r.buf[0]) size = 1 // 將讀出的內容寫入 peekRune 後取反,以便 UnreadRune 能夠撤銷。 r.peekRune = ^rr return } // 讀出的不是單字節字符 var n int // FullRune 的功能不太好理解,總的來講,就是判斷首字符的編碼長度是否完整, // 若是不完整則返回 false,其它狀況都返回 true(包括無效編碼)。 // 循環直到 r.buf[:n] 是完整的 UTF-8 編碼(或無效編碼) for n = 1; !utf8.FullRune(r.buf[:n]); n++ { // 若是字符編碼長度不夠,則再讀出一個字節,繼續判斷 r.buf[n], err = r.readByte() if err != nil { if err == io.EOF { err = nil // 以前有讀出的字節未處理,跳出去處理 break } return } } // 解碼剛讀出的 UTF-8 序列 rr, size = utf8.DecodeRune(r.buf[:n]) if size < n { // 遇到錯誤,保存未處理的字節,用於下一次讀取。 copy(r.pendBuf[r.pending:], r.buf[size:n]) r.pending += n - size } // 將讀出的內容寫入 peekRune 後取反,以便 UnreadRune 能夠撤銷。 r.peekRune = ^rr return } // 實現 io.RuneScanner 接口 func (r *readRune) UnreadRune() error { // 以前執行過 UnreadRune,不能重複執行。 // 只有 UnreadRune 才能讓 peekRune 大於 0。 if r.peekRune >= 0 { return errors.New("fmt: scanning called UnreadRune with no rune available") } // 開始撤銷 // 反轉 peekRune 中的二進制位,使其成爲有效的字符。 r.peekRune = ^r.peekRune return nil } // 臨時對象池 var ssFree = sync.Pool{ New: func() interface{} { return new(ss) }, } // 建立掃描器,或從臨時對象池中獲取一個。 func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { // 從臨時對象池中獲取一個掃描器 s = ssFree.Get().(*ss) // 若是參數 r 不是一個 RuneScanner,則將其包裝成 RuneScanner 再賦值給 s.rs if rs, ok := r.(io.RuneScanner); ok { s.rs = rs } else { // 注意:r 只有 Read 方法,沒有撤銷方法,因此這裏包裝的 readRune 對象 // 沒法將 UnreadRune 所撤銷的內容返回到 r 中。也就是說,儘可能爲 r 提供 // RuneScanner,不然可能形成 r 中的數據丟失。 s.rs = &readRune{reader: r, peekRune: -1} } // 復位參數 s.nlIsSpace = nlIsSpace s.nlIsEnd = nlIsEnd s.atEOF = false s.limit = hugeWid s.argLimit = hugeWid s.maxWid = hugeWid s.validSave = true s.count = 0 return } // 回收掃描器,避免再次分配。 func (s *ss) free(old ssave) { // 若是掃描器被遞歸使用,則只須要恢復舊狀態,而後繼續使用。 if old.validSave { s.ssave = old return } // 不回收緩衝區太大的掃描器,避免內存浪費。 if cap(s.buf) > 1024 { return } // 復位並存入 s.buf = s.buf[:0] s.rs = nil ssFree.Put(s) } // 用於實現 ScanState 接口 func (s *ss) skipSpace(stopAtNewline bool) { for { r := s.getRune() if r == eof { return } // \r\n 當 \n 處理 // peek 判斷即將讀取的字符是否在字符串 "\n" 中(只判斷不讀取)。 if r == '\r' && s.peek("\n") { continue } if r == '\n' { if stopAtNewline { // 換行符以後中止讀取 break } if s.nlIsSpace { // 換行符當空白處理 continue } // 換行符當非空白字符處理 // 在這裏不容許,因此停止整個掃描過程,返回 err。 s.errorString("unexpected newline") return } // 非空白字符,撤銷讀取並返回。 if !isSpace(r) { s.UnreadRune() break } } } // 用於實現 ScanState 接口 func (s *ss) token(skipSpace bool, f func(rune) bool) []byte { if skipSpace { s.skipSpace(false) } // 循環讀取直到不知足 f(r) 或遇到 EOF for { r := s.getRune() if r == eof { break } if !f(r) { s.UnreadRune() break } s.buf.WriteRune(r) } return s.buf } var complexError = errors.New("syntax error scanning complex number") var boolError = errors.New("syntax error scanning boolean") // 返回 r 在 s 中的字符序號(不是字節下標) func indexRune(s string, r rune) int { for i, c := range s { if c == r { return i } } return -1 } // 判斷即將讀取的字符是否在 ok 中。 // 若是 accept 爲 flase 則讀取並丟棄該字符,不管結果如何。 // 若是 accept 爲 true,則根據結果作進一步處理: // 結果爲 true :將字符讀入 s.buf 中 // 結果爲 false:不讀取該字符 func (s *ss) consume(ok string, accept bool) bool { r := s.getRune() if r == eof { return false } // r 在 ok 中 if indexRune(ok, r) >= 0 { if accept { s.buf.WriteRune(r) } return true } // r 不在 ok 中(上面已經判斷過 r == eof,因此這裏不必再次判斷) if r != eof && accept { s.UnreadRune() // 不讀取該字符 } return false } // 判斷即將讀取的字符是否在 ok 中,但不讀取該字符。 func (s *ss) peek(ok string) bool { r := s.getRune() if r != eof { s.UnreadRune() } // 在 ok 中查找 r 的下標,判斷您是否 >= 0 return indexRune(ok, r) >= 0 } // 判斷輸入端是否有數據可讀 // 若是沒有數據可讀,則停止整個掃描過程,返回 err。 func (s *ss) notEOF() { if r := s.getRune(); r == eof { panic(io.EOF) } s.UnreadRune() } // 判斷即將讀取的字符是否在 ok 中,若是在,則將其讀入 s.buf 中, // 並返回 true,不然不讀取,並返回 false。 func (s *ss) accept(ok string) bool { return s.consume(ok, true) } // 判斷 verb 是否在 okVerbs 中, // 若是在,則返回 true。若是不在,則停止整個掃描過程,返回 err。 // 沒有返回 false 的狀況。typ 用於在 err 中指示類型信息。 func (s *ss) okVerb(verb rune, okVerbs, typ string) bool { for _, v := range okVerbs { if v == verb { return true } } s.errorString("bad verb '%" + string(verb) + "' for " + typ) return false } // 從輸入端讀取一個布爾值,verb 必須爲 t 或 v,不然讀取失敗。 // 可探測 0、一、t、f、true、false,忽略大小寫。 func (s *ss) scanBool(verb rune) bool { // 跳過行首空白(包括換行符) s.skipSpace(false) // 輸入端必須有內容可讀 s.notEOF() // 動詞不是 t 或 v,不符合布爾型的要求 if !s.okVerb(verb, "tv", "boolean") { return false } // 布爾型的語法檢測很討厭,咱們不作嚴格要求。 // 若是遇到不完整的 tr、tru 或 fa、fal、fals 則停止整個掃描過程,返回 err。 switch s.getRune() { case '0': return false case '1': return true case 't', 'T': if s.accept("rR") && (!s.accept("uU") || !s.accept("eE")) { s.error(boolError) } return true case 'f', 'F': if s.accept("aA") && (!s.accept("lL") || !s.accept("sS") || !s.accept("eE")) { s.error(boolError) } return false } return false } // 數值元素 const ( binaryDigits = "01" octalDigits = "01234567" decimalDigits = "0123456789" hexadecimalDigits = "0123456789aAbBcCdDeEfF" sign = "+-" period = "." exponent = "eEp" ) // 返回 verb 所表明的進位制,及其字符範圍(即上面的常量) func (s *ss) getBase(verb rune) (base int, digits string) { // 判斷 verb 是否符合整型要求。 // 若是不符合,則停止整個掃描過程,返回 err。 s.okVerb(verb, "bdoUxXv", "integer") base = 10 // 默認爲十進制 digits = decimalDigits switch verb { case 'b': // 二進制 base = 2 digits = binaryDigits case 'o': // 八進制 base = 8 digits = octalDigits case 'x', 'X', 'U': // 十六進制 base = 16 digits = hexadecimalDigits } return } // 從輸入端讀取數值字符串到 s.buf 中。 // digits 是可接收的字符範圍(不一樣進位制有不一樣的字符範圍) // haveDigits 表示 s.buf 中是否已經有數值存在, // 若是沒有,則本方法必須讀出數值,不然停止整個掃描過程,返回 err。 func (s *ss) scanNumber(digits string, haveDigits bool) string { if !haveDigits { // 輸入端必須有內容可讀 s.notEOF() if !s.accept(digits) { // 若是沒有讀到指定進制的字符,則停止整個掃描過程,返回 err。 s.errorString("expected integer") } } // 繼續讀取合格的字符,存入 s.buf 中 for s.accept(digits) { } // 返回讀出的字符串 return string(s.buf) } // 功能同 ReadRune,只不過經過 bitSize 限制讀取字符的位寬。 // 若是讀出的字符在指定位寬內,則返回,不然停止整個掃描過程,返回 err。 func (s *ss) scanRune(bitSize int) int64 { s.notEOF() r := int64(s.getRune()) n := uint(bitSize) // 位寬判斷 x := (r << (64 - n)) >> (64 - n) if x != r { s.errorString("overflow on character value " + string(r)) } return r } // 根據輸入端的前導符 0 或 0x 判斷進位制並返回,同時返回字符範圍。 // found 表示檢測到前導符。只有當動詞是 %v 的時候纔會被調用。 func (s *ss) scanBasePrefix() (base int, digits string, found bool) { // 若是不是以 0 開頭,表示是十進制數 if !s.peek("0") { return 10, decimalDigits, false } // 若是是 0 開頭,則將其讀入 s.buf 中 s.accept("0") found = true // 已經讀出一個 0,若是前導符後面沒有數值,將使用該 0 值。 // 繼續判斷是八進制仍是十六進制 base, digits = 8, octalDigits if s.peek("xX") { s.consume("xX", false) // 丟棄匹配的 x 或 X 字符 base, digits = 16, hexadecimalDigits } return } // 讀取一個 int64 整數。bitSize 用於限制整數的位寬。 // 若是讀出的整數在指定位寬內,則返回,不然停止整個掃描過程,返回 err。 func (s *ss) scanInt(verb rune, bitSize int) int64 { // 只須要讀取一個字符 if verb == 'c' { return s.scanRune(bitSize) } s.skipSpace(false) s.notEOF() // 根據不一樣的動詞獲取進位制信息 base, digits := s.getBase(verb) haveDigits := false // 是否已經讀出數值 if verb == 'U' { // 丟棄前導符 U+ // 若是沒有讀取到 U+ 則停止整個掃描過程,返回 err。 if !s.consume("U", false) || !s.consume("+", false) { s.errorString("bad unicode format ") } } else { // sign 是常量 +-,若是能讀取到符號,則將其存入 s.buf 中。 s.accept(sign) if verb == 'v' { // 根據輸入端的前導符 0 或 0x 肯定進位制 // 若是有前導符,則已經讀出一個 0,前導符後面能夠沒有數值。 base, digits, haveDigits = s.scanBasePrefix() } } // 讀出數值字符串,若是讀取失敗,則停止整個掃描過程,返回 err。 tok := s.scanNumber(digits, haveDigits) // 轉換爲整型 i, err := strconv.ParseInt(tok, base, 64) if err != nil { s.error(err) } // 位寬判斷 n := uint(bitSize) x := (i << (64 - n)) >> (64 - n) if x != i { s.errorString("integer overflow on token " + tok) } return i } // 功能同 scanInt,只不過返回的是無符號整數。 func (s *ss) scanUint(verb rune, bitSize int) uint64 { if verb == 'c' { return uint64(s.scanRune(bitSize)) } s.skipSpace(false) s.notEOF() base, digits := s.getBase(verb) haveDigits := false if verb == 'U' { if !s.consume("U", false) || !s.consume("+", false) { s.errorString("bad unicode format ") } } else if verb == 'v' { base, digits, haveDigits = s.scanBasePrefix() } tok := s.scanNumber(digits, haveDigits) i, err := strconv.ParseUint(tok, base, 64) if err != nil { s.error(err) } n := uint(bitSize) x := (i << (64 - n)) >> (64 - n) if x != i { s.errorString("unsigned integer overflow on token " + tok) } return i } // 讀取一個浮點數,若是指定了寬度值,則不會超過寬度值。 // 沒有檢查「只有指數沒有小數」的狀況,可是 Atof 會進行檢查。 func (s *ss) floatToken() string { s.buf = s.buf[:0] // 非數值 NAN if s.accept("nN") && s.accept("aA") && s.accept("nN") { return string(s.buf) } // 符號 +- s.accept(sign) // 無窮大 INF if s.accept("iI") && s.accept("nN") && s.accept("fF") { return string(s.buf) } // 整數部分 for s.accept(decimalDigits) { } // 小數點 if s.accept(period) { // 小數部分 for s.accept(decimalDigits) { } } // 指數標誌 if s.accept(exponent) { // 指數符號 s.accept(sign) // 指數值 for s.accept(decimalDigits) { } } return string(s.buf) } // 讀出一個虛數的實部和虛部。 // 虛數能夠加上括號,虛數格式必須爲 N+Ni,N 必須是浮點數,中間不能有空格。 func (s *ss) complexTokens() (real, imag string) { // TODO: 未實現純實部和純虛部的讀取 parens := s.accept("(") // 登記是否以 "(" 開頭 real = s.floatToken() // 讀取實部 s.buf = s.buf[:0] // 虛部必須有符號 if !s.accept("+-") { // 讀取符號到 s.buf s.error(complexError) } imagSign := string(s.buf) // 取出符號 imag = s.floatToken() // 讀取虛部 if !s.accept("i") { // 虛部後面必須爲 i s.error(complexError) } // 若是以 "(" 開頭,則必須以 ")" 結尾。 if parens && !s.accept(")") { s.error(complexError) } return real, imagSign + imag } // 將一個字符串轉換爲 float64 類型的值。 // str 要轉換的字符串,n:要轉換出的浮點數類型(32 或 64) // 若是轉換失敗,則停止整個掃描過程,返回 err。 func (s *ss) convertFloat(str string, n int) float64 { // Atof 不處理以 2 爲底的指數,可是它們很容易計算。 if p := indexRune(str, 'p'); p >= 0 { // 獲取小數部分 f, err := strconv.ParseFloat(str[:p], n) if err != nil { if e, ok := err.(*strconv.NumError); ok { e.Num = str } s.error(err) } // 獲取指數部分 m, err := strconv.Atoi(str[p+1:]) if err != nil { // if e, ok := err.(*strconv.NumError); ok { e.Num = str } s.error(err) } // 算出結果:f * (2 的 m 次方) return math.Ldexp(f, m) } // 普通浮點數直接轉換 f, err := strconv.ParseFloat(str, n) if err != nil { s.error(err) } return f } // 讀取一個 complex128 類型的值。 func (s *ss) scanComplex(verb rune, n int) complex128 { // 檢查 verb 的有效性(floatVerbs 是常量 "beEfFgGv") if !s.okVerb(verb, floatVerbs, "complex") { return 0 } s.skipSpace(false) s.notEOF() // 讀取實部和虛部 sreal, simag := s.complexTokens() real := s.convertFloat(sreal, n/2) imag := s.convertFloat(simag, n/2) return complex(real, imag) } // 讀取一個字符串。 func (s *ss) convertString(verb rune) (str string) { // 檢查 verb 的有效性 if !s.okVerb(verb, "svqxX", "string") { return "" } s.skipSpace(false) s.notEOF() switch verb { case 'q': // 帶引號字符串 str = s.quotedString() case 'x', 'X': // 十六進制格式的字符串 str = s.hexString() default: // %s 和 %v 僅返回連續的非空白字符 str = string(s.token(true, notSpace)) } return } // 讀取雙引號或反引號字符串。 func (s *ss) quotedString() string { s.notEOF() quote := s.getRune() switch quote { case '`': // 讀取直到遇到下一個反引號或 EOF for { r := s.mustReadRune() if r == quote { break } s.buf.WriteRune(r) } return string(s.buf) case '"': s.buf.WriteByte('"') for { r := s.mustReadRune() s.buf.WriteRune(r) if r == '\\' { // strconv.Unquote 會處理轉義字符,這裏只須要寫入。 s.buf.WriteRune(s.mustReadRune()) } else if r == '"' { break } } result, err := strconv.Unquote(string(s.buf)) if err != nil { s.error(err) } return result default: s.errorString("expected quoted string") } return "" } // hexDigit 返回十六進制字符所表明的十進制值 func hexDigit(d rune) (int, bool) { digit := int(d) switch digit { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': return digit - '0', true case 'a', 'b', 'c', 'd', 'e', 'f': return 10 + digit - 'a', true case 'A', 'B', 'C', 'D', 'E', 'F': return 10 + digit - 'A', true } return -1, false } // 讀取兩個十六進制字符,並返回其所表示的字節。 // b :讀取的字節 // ok:是否讀取成功 // 若是缺乏後一個字符,則停止整個掃描過程,返回 err。 func (s *ss) hexByte() (b byte, ok bool) { // 處理第一個字符 rune1 := s.getRune() if rune1 == eof { return } value1, ok := hexDigit(rune1) if !ok { s.UnreadRune() return } // 處理第二個字符 value2, ok := hexDigit(s.mustReadRune()) if !ok { s.errorString("illegal hex digit") return } // 轉換爲十進制數值 return byte(value1<<4 | value2), true } // 讀取十六進制字符串,並返回其所表示的內容 // 兩個十六進制字符表示一個字節 // 讀取失敗則停止整個掃描過程,返回 err。 func (s *ss) hexString() string { s.notEOF() for { // 讀取兩個十六進制字符所表示的一個字節 b, ok := s.hexByte() if !ok { break } s.buf.WriteByte(b) } if len(s.buf) == 0 { s.errorString("no hex data for %x string") return "" } return string(s.buf) } const ( floatVerbs = "beEfFgGv" hugeWid = 1 << 30 intBits = 32 << (^uint(0) >> 63) uintptrBits = 32 << (^uintptr(0) >> 63) ) // 處理一個 arg。 // 遇到錯誤則停止整個掃描過程,返回 err。 func (s *ss) scanOne(verb rune, arg interface{}) { s.buf = s.buf[:0] var err error // 若是參數有它本身的 Scan 方法,則調用它。 if v, ok := arg.(Scanner); ok { err = v.Scan(s, verb) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } s.error(err) } return } // 根據不一樣的 arg 類型選擇不一樣的解析方法。 switch v := arg.(type) { case *bool: *v = s.scanBool(verb) case *complex64: *v = complex64(s.scanComplex(verb, 64)) case *complex128: *v = s.scanComplex(verb, 128) case *int: *v = int(s.scanInt(verb, intBits)) case *int8: *v = int8(s.scanInt(verb, 8)) case *int16: *v = int16(s.scanInt(verb, 16)) case *int32: *v = int32(s.scanInt(verb, 32)) case *int64: *v = s.scanInt(verb, 64) case *uint: *v = uint(s.scanUint(verb, intBits)) case *uint8: *v = uint8(s.scanUint(verb, 8)) case *uint16: *v = uint16(s.scanUint(verb, 16)) case *uint32: *v = uint32(s.scanUint(verb, 32)) case *uint64: *v = s.scanUint(verb, 64) case *uintptr: *v = uintptr(s.scanUint(verb, uintptrBits)) case *float32: if s.okVerb(verb, floatVerbs, "float32") { s.skipSpace(false) s.notEOF() *v = float32(s.convertFloat(s.floatToken(), 32)) } case *float64: if s.okVerb(verb, floatVerbs, "float64") { s.skipSpace(false) s.notEOF() *v = s.convertFloat(s.floatToken(), 64) } case *string: *v = s.convertString(verb) case *[]byte: // 先掃描成字符串,而後再轉換爲 []byte,因此獲得的是一個副本, // 若是咱們掃描成 []byte,那麼結果將指向緩衝區。 *v = []byte(s.convertString(verb)) default: val := reflect.ValueOf(v) ptr := val // arg 必須是一個指針,就像其它 arg 那樣 if ptr.Kind() != reflect.Ptr { s.errorString("type not a pointer: " + val.Type().String()) return } // 接下來的流程和上面同樣 switch v := ptr.Elem(); v.Kind() { case reflect.Bool: v.SetBool(s.scanBool(verb)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: v.SetInt(s.scanInt(verb, v.Type().Bits())) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: v.SetUint(s.scanUint(verb, v.Type().Bits())) case reflect.String: v.SetString(s.convertString(verb)) case reflect.Slice: typ := v.Type() // 對於切片,只能處理 []byte 的別名類型。 if typ.Elem().Kind() != reflect.Uint8 { s.errorString("can't scan type: " + val.Type().String()) } // 解析出字符串 str := s.convertString(verb) // 轉換爲字節切片返回 v.Set(reflect.MakeSlice(typ, len(str), len(str))) for i := 0; i < len(str); i++ { v.Index(i).SetUint(uint64(str[i])) } case reflect.Float32, reflect.Float64: s.skipSpace(false) s.notEOF() v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits())) case reflect.Complex64, reflect.Complex128: v.SetComplex(s.scanComplex(verb, v.Type().Bits())) default: s.errorString("can't scan type: " + val.Type().String()) } } } // 將本地引起的 panic(scanError 類型)和 EOF panic 轉換爲 error。 func errorHandler(errp *error) { if e := recover(); e != nil { // 本地 panic 轉換爲 error if se, ok := e.(scanError); ok { *errp = se.err // EOF panic 也轉換爲 error } else if eof, ok := e.(error); ok && eof == io.EOF { *errp = eof } else { panic(e) } } } // 掃描器的掃描過程 func (s *ss) doScan(a []interface{}) (numProcessed int, err error) { defer errorHandler(&err) // 循環處理全部 arg for _, arg := range a { s.scanOne('v', arg) numProcessed++ } // 全部參數掃描結束 // 檢查是否以換行符或 EOF 結尾(Scanln 等須要這個錯誤信息) if s.nlIsEnd { for { r := s.getRune() if r == '\n' || r == eof { break } // 跳過空白字符後再次判斷 if !isSpace(r) { s.errorString("expected newline") break } } } return } // 處理非佔位字符串,返回已處理的字節數。處理結果分爲如下幾種狀況: // 遇到佔位符 :返回 % 以前的字節數 // 不匹配 :返回 -1 // 徹底匹配(format 被讀完):返回 len(foramt) // 輸入端被讀完 :強行停止掃描 // advance 的邏輯比較複雜,要配合 doScanf 理解,很難徹底理解。 func (s *ss) advance(format string) (i int) { // 這裏的 format 不是完整的格式字符串,而是由 doScanf 提供的 // 未處理部分的格式字符串。doScanf 處理完一個佔位符後,就把 // 剩下的格式字符串交給 advance 處理。 for i < len(format) { // 解碼一個待處理字符 fmtc, w := utf8.DecodeRuneInString(format[i:]) // 一、處理遇到的 % 號 if fmtc == '%' { // 不能以 % 結尾 if i+w == len(format) { s.errorString("missing verb: % at end of format string") } nextc, _ := utf8.DecodeRuneInString(format[i+w:]) // 遇到單獨的 %(佔位符)則返回 % 的下標 i(即 % 以前已處理的字節數) if nextc != '%' { return } // %% 被解析爲一個 %,當作普通字符,交給後面處理 i += w // 跳過 %% 中的前一個 % } // 二、處理 format 中的連續空白字符 sawSpace := false // 是否遇到連續的空白字符(包括換行符) wasNewline := false // 是否遇到換行符 // 跳過連續的空白符 for isSpace(fmtc) && i < len(format) { if fmtc == '\n' { if wasNewline { // 一次只處理一個換行符,以後的換行符交給後面處理 break } // 登記遇到換行符 wasNewline = true } // 登記遇到空白字符 sawSpace = true i += w // 跳過已處理的空白字符 // 更新待處理字符 fmtc, w = utf8.DecodeRuneInString(format[i:]) } // 到此,表示沒有連續空白或已跳過連續空白, // 此時 i 指向非空白字符或換行符(即前面遇到的未處理的換行符)。 // 三、對比輸入端的連續空白字符 if sawSpace { inputc := s.getRune() if inputc == eof { // 輸入端被讀空,返回已處理的字節數。 // 返回後,在 doScanf 中繼續判斷 format 是否也被讀完。 return } // 輸入端未遇到空白字符,匹配失敗,停止整個掃描過程,返回 err。 if !isSpace(inputc) { s.errorString("expected space in input to match format") } // 輸入端也遇到空白字符,跳過空白部分。 for inputc != '\n' && isSpace(inputc) { inputc = s.getRune() } // 此時 inputc 有可能爲 eof // 輸入端遇到換行符 if inputc == '\n' { // format 中未遇到換行符,匹配失敗,停止整個掃描過程,返回 err。 if !wasNewline { s.errorString("newline in input does not match format") } // 到此,輸入端和 format 中都遇到換行符,匹配成功。 // 輸入端換行符以後的空白沒有繼續處理,而 format 中卻處理了, // 這將致使 "\n a %d" 沒法匹配 "\n a 1"。使用的時候要注意。 // 匹配完畢,返回已處理的字節數 // 這裏把 \n 當作一次掃描結束,這種行爲相似於 Scanln。 return } // 輸入端空白字符處理完畢,未遇到換行符,則讀取的應該是非空白字符。 // 撤銷對非空白字符的讀取,交給下一輪去處理。 // 若是以前讀取的是 eof 則 UnreadRune 不會撤銷任何內容。 s.UnreadRune() // format 中遇到換行符,與輸入端不匹配 if wasNewline { s.errorString("newline in format does not match input") } // 空白部分(第二個換行符以前的)所有匹配成功,繼續下一輪,處理後面的字符。 continue } // 到此,表示 format 中沒遇到空白字符或空白字符已經處理完畢。 // 四、處理 format 中的非空白字符 // 使用 mustReadRune 而不是 getRune 表示若是讀取失敗(EOF), // 則停止整個掃描過程,返回 err。 inputc := s.mustReadRune() // 非空白字符匹配失敗,撤銷對 input 的讀取,並返回 -1 if fmtc != inputc { // 匹配失敗,應該不須要再作什麼了,不過 advance 做爲一個獨立的功能函數, // 仍是要嚴謹一些,執行 s.UnreadRune 是爲了保證輸入端中已處理的內容與 // format 中 i 的位置對齊。 s.UnreadRune() return -1 } // 非空白字符匹配成功,繼續處理下一個字符。 i += w } // 所有處理完畢,返回 len(format) return } // 掃描器的格式化掃描過程 func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err error) { // 消化本地 panic,結束整個掃描過程。 defer errorHandler(&err) end := len(format) - 1 for i := 0; i <= end; { // 先處理 format 中的非佔位符部分。 w := s.advance(format[i:]) // 循環直到遇到 % 字符 if w > 0 { i += w continue // 這裏有一個做用,就是當 i == len(format) 時, // 會終止循環,不會繼續在後面訪問 format[i] } // 到這裏,表示 format 沒有處理完,並且應該處理佔位符了。 // 沒有遇找到佔位符,看看是什麼緣由 if format[i] != '%' { // 非空白字符匹配失敗 if w < 0 { s.errorString("input does not match format") } // 到此,表示遇到 EOF // 不過代碼不會執行到這裏,由於在 advance 中 EOF 會引起 panic // 爲了邏輯的嚴謹,這裏仍是須要添加一個 break,以防 advance 發生改變。 break } i++ // 跳過 % 號 // 讀取佔位符中的寬度信息 var widPresent bool s.maxWid, widPresent, i = parsenum(format, i, end) // 若是沒有設置寬度信息,則將寬度設置爲默認值 hugeWid if !widPresent { s.maxWid = hugeWid // hugeWid 是常量 1 << 30 } // 獲取動詞 c, w := utf8.DecodeRuneInString(format[i:]) i += w // 跳過動詞 // 若是動詞不是 c,則跳過輸入端開頭的空白 if c != 'c' { s.SkipSpace() } // 默認讀取限制 s.argLimit = s.limit // 根據佔位符中的寬度信息設置輸入端容許讀出的最大字符數 if f := s.count + s.maxWid; f < s.argLimit { s.argLimit = f } // arg 太少,佔位符太多,數量不匹配。 if numProcessed >= len(a) { s.errorString("too few operands for format '%" + format[i-w:] + "'") break } arg := a[numProcessed] s.scanOne(c, arg) // 處理一個 arg numProcessed++ // 跳過已處理的 arg // 恢復默認讀取限制 s.argLimit = s.limit } // arg 太多,佔位符太少,數量不匹配。 if numProcessed < len(a) { s.errorString("too many operands") } return }