1. 程式人生 > >Go語言開發(十一)、Go語言常用標準庫一

Go語言開發(十一)、Go語言常用標準庫一

lena unix doc 計算 cmd.run ner rem 信息 前綴

Go語言開發(十一)、Go語言常用標準庫一

一、log

1、log模塊簡介

Go語言中log模塊用於在程序中輸出日誌。
log模塊提供了三類日誌輸出接口,Print、Fatal和Panic。Print是普通輸出;Fatal是在執行完Print後,執行 os.Exit(1);Panic是在執行完Print後調用panic()方法。log模塊對每一類接口其提供了3中調用方式,分別是"Xxxx、 Xxxxln、Xxxxf"。

2、log.Print接口

log.Print類接口包括log.Print、log.Println、log.Printf,接口如下:

// Printf calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Printf(format string, v ...interface{}) {
   l.Output(2, fmt.Sprintf(format, v...))
}

// Print calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) }

// Println calls l.Output to print to the logger.
// Arguments are handled in the manner of fmt.Println.
func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) }

log.Print類接口使用示例:

package main

import (
   "log"
)

func logPrintTest(){
   arr := []int {2,3}
   log.Print("Print array ",arr,"\n")
   log.Println("Println array",arr)
   log.Printf("Printf array with item [%d,%d]\n",arr[0],arr[1])
}

func main() {
   logPrintTest()
}
// output
// 2018/10/06 13:38:29 Print array [2 3]
// 2018/10/06 13:38:29 Println array [2 3]
// 2018/10/06 13:38:29 Printf array with item [2,3]

3、log.Fatal接口

log.Fatal類接口包括log.Fatal、log.Fatalln、log.Fatalf,接口如下:

// Fatal is equivalent to l.Print() followed by a call to os.Exit(1).
func (l *Logger) Fatal(v ...interface{}) {
   l.Output(2, fmt.Sprint(v...))
   os.Exit(1)
}

// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1).
func (l *Logger) Fatalf(format string, v ...interface{}) {
   l.Output(2, fmt.Sprintf(format, v...))
   os.Exit(1)
}

// Fatalln is equivalent to l.Println() followed by a call to os.Exit(1).
func (l *Logger) Fatalln(v ...interface{}) {
   l.Output(2, fmt.Sprintln(v...))
   os.Exit(1)
}

?log.Fata接口會先將日誌內容打印到標準輸出,接著調用系統的?os.exit(1)接口,退出程序並返回狀態1 。但由於直接調用系統接口退出,defer函數不會被調用。
?log.Fatal接口使用示例:

package main

import (
   "log"
   "fmt"
)

func logFatalTest(){
   level := "Fatal"
   defer func() {
      fmt.Println("defer Fatal")
   }()
   log.Fatal("print ",level, " level\n")
   // 後續不會被執行
   log.Fatalln("print ",level)
   log.Fatalf("print %s level", level)
}

func main() {
   logFatalTest()
}
// output
// 2018/10/06 13:47:57 print Fatal level

?log.Fatal接口調用會導致程序退出。

4、log.Panic接口

log.Panic類接口包括log.Panic、log.Panicln、log.Panicf,接口如下:

// Panic is equivalent to l.Print() followed by a call to panic().
func (l *Logger) Panic(v ...interface{}) {
   s := fmt.Sprint(v...)
   l.Output(2, s)
   panic(s)
}

// Panicf is equivalent to l.Printf() followed by a call to panic().
func (l *Logger) Panicf(format string, v ...interface{}) {
   s := fmt.Sprintf(format, v...)
   l.Output(2, s)
   panic(s)
}

// Panicln is equivalent to l.Println() followed by a call to panic().
func (l *Logger) Panicln(v ...interface{}) {
   s := fmt.Sprintln(v...)
   l.Output(2, s)
   panic(s)
}

log.Panic接口函數把日誌內容刷到標準錯誤後調用panic函數。
log.Panic接口使用示例:

package main

import (
   "log"
   "fmt"
)

func logPanicTest(){
   level := "Panic"
   defer func() {
      fmt.Println("defer Panic 1")
      if err := recover(); err != nil {
         fmt.Println(err)
      }
   }()
   log.Panic(level, " level")
   defer func() {
      fmt.Println("defer Panic 2")
   }()
}

func main() {
   logPanicTest()
}
// output
// defer Panic 1
// 2018/10/06 13:55:17 Panic level
// Panic level

第一個defer函數被調用並輸出“defer Panic 1”,Panic後聲明的defer不會執行。

5、logger定制

Logger是寫入日誌的基本組件,log模塊中存在一個標準Logger,可以直接通過log進行訪問,可以直接使用log.xxxx進行日誌進行輸出。但在實際使用中,不同類型的日誌可能擁有需求,僅標準Logger不能滿足日誌記錄的需求,通過創建不同的Logger可以將不同類型的日誌分類輸出。使用logger前需要首先通過New函數創建一個Logger對象,函數聲明如下:

func New(out io.Writer, prefix string, flag int) *Logger {
   return &Logger{out: out, prefix: prefix, flag: flag}
}

函數接收三個參數分別是日誌輸出的IO對象,日誌前綴和日誌包含的通用信息標識位,通過對參數進行設置可以對Logger進行定制。其中IO對象通常是標準輸出os.Stdout,os.Stderr,或者綁定到文件的IO。日誌前綴和信息標識位可以對日誌的格式進行設置。
一條日誌由三個部分組成,其結構如下:
{日誌前綴} {標識1} {標識2} ... {標識n} {日誌內容}
日誌前綴,通過prefix參數設置,可以是任意字符串。
標識,通過flags參數設置,當某個標識被設置,會在日誌中進行顯示,log模塊中定義了如下標識,多個標識通過按位或進行組合:
Ldate 顯示當前日期(當前時區)
Ltime 顯示當前時間(當前時區)
Lmicroseconds 顯示當前時間(微秒)
Llongfile 包含路徑的完整文件名
Lshortfile 不包含路徑的文件名
LUTC Ldata和Ltime使用UTC時間
LstdFlags 標準Logger的標識,等價於 Ldate | Ltime
Logger相關接口如下:

// Flags returns the output flags for the logger.
func (l *Logger) Flags() int {
   l.mu.Lock()
   defer l.mu.Unlock()
   return l.flag
}

// SetFlags sets the output flags for the logger.
func (l *Logger) SetFlags(flag int) {
   l.mu.Lock()
   defer l.mu.Unlock()
   l.flag = flag
}

// Prefix returns the output prefix for the logger.
func (l *Logger) Prefix() string {
   l.mu.Lock()
   defer l.mu.Unlock()
   return l.prefix
}

// SetPrefix sets the output prefix for the logger.
func (l *Logger) SetPrefix(prefix string) {
   l.mu.Lock()
   defer l.mu.Unlock()
   l.prefix = prefix
}

// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
   std.mu.Lock()
   defer std.mu.Unlock()
   std.out = w
}

// Flags returns the output flags for the standard logger.
func Flags() int {
   return std.Flags()
}

// SetFlags sets the output flags for the standard logger.
func SetFlags(flag int) {
   std.SetFlags(flag)
}

// Prefix returns the output prefix for the standard logger.
func Prefix() string {
   return std.Prefix()
}

// SetPrefix sets the output prefix for the standard logger.
func SetPrefix(prefix string) {
   std.SetPrefix(prefix)
}

Logger示例如下:

package main

import (
   "log"
   "os"
)

func main() {
   prefix := "[INFO]"
   logger := log.New(os.Stdout, prefix, log.LstdFlags | log.Lshortfile)
   logger.Print("Hello Go")
   logger.SetPrefix("[OUTPUT]")
   logger.SetFlags(log.LstdFlags)
   logger.Print("Hello Logger")
}
// output
// [INFO]2018/10/06 13:24:44 main.go:11: Hello Go
// [OUTPUT]2018/10/06 13:24:44 Hello Logger

6、log分級日誌實現

Go的log模塊沒有對日誌進行分級的功能,對於日誌分級需求可以在log的基礎上進行實現。通過使用log模塊的基礎功能進行封裝,可以實現Log4類似的日誌功能。

package logger

import (
"fmt"
"log"
"os"
"os/exec"
"strings"
"time"
)

const (
   PanicLevel int = iota
   FatalLevel
   ErrorLevel
   WarnLevel
   InfoLevel
   DebugLevel
)

type LogFile struct {
   level    int
   logTime  int64
   fileName string
   fileFd   *os.File
}

var logFile LogFile

func Config(logFolder string, level int) {
   logFile.fileName = logFolder
   logFile.level = level

   log.SetOutput(logFile)
   log.SetFlags(log.Lmicroseconds | log.Lshortfile)
}

func SetLevel(level int) {
   logFile.level = level
}

func Debugf(format string, args ...interface{}) {
   if logFile.level >= DebugLevel {
      log.SetPrefix("debug ")
      log.Output(2, fmt.Sprintf(format, args...))
   }
}

func Infof(format string, args ...interface{}) {
   if logFile.level >= InfoLevel {
      log.SetPrefix("info ")
      log.Output(2, fmt.Sprintf(format, args...))
   }
}

func Warnf(format string, args ...interface{}) {
   if logFile.level >= WarnLevel {
      log.SetPrefix("warn ")
      log.Output(2, fmt.Sprintf(format, args...))
   }
}

func Errorf(format string, args ...interface{}) {
   if logFile.level >= ErrorLevel {
      log.SetPrefix("error ")
      log.Output(2, fmt.Sprintf(format, args...))
   }
}

func Fatalf(format string, args ...interface{}) {
   if logFile.level >= FatalLevel {
      log.SetPrefix("fatal ")
      log.Output(2, fmt.Sprintf(format, args...))
   }
}

func (me LogFile) Write(buf []byte) (n int, err error) {
   if me.fileName == "" {
      fmt.Printf("consol: %s", buf)
      return len(buf), nil
   }

   if logFile.logTime+3600 < time.Now().Unix() {
      logFile.createLogFile()
      logFile.logTime = time.Now().Unix()
   }

   if logFile.fileFd == nil {
      return len(buf), nil
   }

   return logFile.fileFd.Write(buf)
}

func (me *LogFile) createLogFile() {
   logdir := "./"
   if index := strings.LastIndex(me.fileName, "/"); index != -1 {
      logdir = me.fileName[0:index] + "/"
      os.MkdirAll(me.fileName[0:index], os.ModePerm)
   }

   now := time.Now()
   filename := fmt.Sprintf("%s_%04d%02d%02d_%02d%02d", me.fileName, now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute())
   if err := os.Rename(me.fileName, filename); err == nil {
      go func() {
         tarCmd := exec.Command("tar", "-zcf", filename+".tar.gz", filename, "--remove-files")
         tarCmd.Run()

         rmCmd := exec.Command("/bin/sh", "-c", "find "+logdir+` -type f -mtime +2 -exec rm {} \;`)
         rmCmd.Run()
      }()
   }

   for index := 0; index < 10; index++ {
      if fd, err := os.OpenFile(me.fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModeExclusive); nil == err {
         me.fileFd.Sync()
         me.fileFd.Close()
         me.fileFd = fd
         break
      }

      me.fileFd = nil
   }
}

上述logger模塊封裝的功能如下:
A、支持歸檔輸出,一個小時壓縮歸檔一份
B、最多保留三天的日誌
C、支持日誌級別自定義
D、如果沒有指定輸出文件默認輸出到控制臺。
E、支持輸出文件名行號,以及時間、日誌界別?

二、regexp

1、regexp簡介

Go語言通過regexp標準包為正則表達式提供了官方支持。在Go語言環境中可以使用下列命令查看正則表達式的語法:
go doc regexp/syntax

2、Regexp正則對象

Regexp是一個編譯好的正則表達式對象。

type Regexp struct {
   // read-only after Compile
   regexpRO

   // cache of machines for running regexp
   mu      sync.Mutex
   machine []*machine
}

通常需要使用正則表達式構建一個正則對象

3、regexp常用接口

func Match(pattern string, b []byte) (matched bool, err error)
func MatchString(pattern string, s string) (matched bool, err error)
func MatchReader(pattern string, r io.RuneReader) (matched bool, err error)

判斷在b中能否找到pattern所匹配的字符串
func Compile(expr string) (*Regexp, error)
將正則表達式編譯成一個正則對象(使用PERL語法)。
該正則對象會采用“leftmost-first”模式。選擇第一個匹配結果。
如果正則表達式語法錯誤,則返回錯誤信息。
func CompilePOSIX(expr string) (*Regexp, error)
將正則表達式編譯成一個正則對象(正則語法限制在 POSIX ERE 範圍內)。
該正則對象會采用“leftmost-longest”模式。選擇最長的匹配結果。
POSIX 語法不支持Perl的語法格式:\d、\D、\s、\S、\w、\W
如果正則表達式語法錯誤,則返回錯誤信息。

func MustCompile(str string) *Regexp
func MustCompilePOSIX(str string) *Regexp

將正則表達式編譯成一個正則對象,但會在解析失敗時panic
func (re *Regexp) Longest()
讓正則表達式在之後的搜索中都采用“leftmost-longest”模式。
func (re *Regexp) String() string
返回編譯時使用的正則表達式字符串
func (re *Regexp) NumSubexp() int
返回正則表達式中分組的數量
func (re *Regexp) SubexpNames() []string
返回正則表達式中分組的名字
第 0 個元素表示整個正則表達式的名字,永遠是空字符串。
func (re *Regexp) LiteralPrefix() (prefix string, complete bool)
返回正則表達式必須匹配到的字面前綴(不包含可變部分)。
如果整個正則表達式都是字面值,則 complete 返回 true。

4、regexp示例

package main

import (
   "regexp"
   "fmt"
)

var mailRegexp = regexp.MustCompile(`([A-Za-z0-9]+)@([A-Za-z0-9.]+)\.([A-Za-z0-9.]+)`)

func main() {
   s := " hello‘s email is [email protected]"
   matches := mailRegexp.FindStringSubmatch(s)
   fmt.Println(matches)// [[email protected] hellogo gmail com]
   fmt.Println(matches[0]) // [email protected]
   fmt.Println(matches[1]) // hellogo
   fmt.Println(matches[2]) // gmail
   fmt.Println(matches[3]) // com
}

三、strconv

1、strconv簡介

strconv提供了字符串與基本類型的轉換函數接口。

2、字符串與bool類型轉換

func ParseBool(str string) (value bool, err error)
ParseBool將字符串轉換為布爾值。接受真值:1, t, T, TRUE, true, True;接受假值:0, f, F, FALSE, false, False;其它任何值都返回一個錯誤。

package main

import (
   "fmt"
   "strconv"
)

func main() {
   fmt.Println(strconv.ParseBool("1"))    // true
   fmt.Println(strconv.ParseBool("t"))    // true
   fmt.Println(strconv.ParseBool("T"))    // true
   fmt.Println(strconv.ParseBool("true")) // true
   fmt.Println(strconv.ParseBool("True")) // true
   fmt.Println(strconv.ParseBool("TRUE")) // true
   fmt.Println(strconv.ParseBool("TRue"))
   // false strconv.ParseBool: parsing "TRue": invalid syntax
   fmt.Println(strconv.ParseBool("0"))     // false
   fmt.Println(strconv.ParseBool("f"))     // false
   fmt.Println(strconv.ParseBool("F"))     // false
   fmt.Println(strconv.ParseBool("false")) // false
   fmt.Println(strconv.ParseBool("False")) // false
   fmt.Println(strconv.ParseBool("FALSE")) // false
   fmt.Println(strconv.ParseBool("FALse"))
   // false strconv.ParseBool: parsing "FAlse": invalid syntax
}

func FormatBool(b bool) string
FormatBool將布爾值轉換為字符串"true"或"false"

package main

import (
   "fmt"
   "strconv"
)

func main() {
   fmt.Println(strconv.FormatBool(0 < 1)) // true
   fmt.Println(strconv.FormatBool(0 > 1)) // false
}

func AppendBool(dst []byte, b bool) []byte
AppendBool將布爾值b轉換為字符串"true"或"false",然後將結果追加到dst的尾部,返回追加後的[]byte。

package main

import (
   "fmt"
   "strconv"
)

func main() {
   rst := make([]byte, 0)
   rst = strconv.AppendBool(rst, 0 < 1)
   fmt.Printf("%s\n", rst) // true
   rst = strconv.AppendBool(rst, 0 > 1)
   fmt.Printf("%s\n", rst) // truefalse
}

3、字符串與整型轉換

func ParseInt(s string, base int, bitSize int) (i int64, err error)
ParseInt將字符串轉換為int類型
s:要轉換的字符串
base:進位制(2進制到36進制)
bitSize:指定整數類型(0:int、8:int8、16:int16、32:int32、64:int64)
返回轉換後的結果和轉換時遇到的錯誤,如果base為0,則根據字符串的前綴判斷進位制(0x:16,0:8,其它:10)。

package main

import (
   "fmt"
   "strconv"
)

func main() {
   fmt.Println(strconv.ParseInt("123", 10, 8))
   // 123
   fmt.Println(strconv.ParseInt("12345", 10, 8))
   // 127 strconv.ParseInt: parsing "12345": value out of range
   fmt.Println(strconv.ParseInt("2147483647", 10, 0))
   // 2147483647
   fmt.Println(strconv.ParseInt("0xFF", 16, 0))
   // 0 strconv.ParseInt: parsing "0xFF": invalid syntax
   fmt.Println(strconv.ParseInt("FF", 16, 0))
   // 255
   fmt.Println(strconv.ParseInt("0xFF", 0, 0))
   // 255
}

func ParseUint(s string, base int, bitSize int) (n uint64, err error)
ParseUint將字符串轉換為uint類型

package main

import (
   "fmt"
   "strconv"
)

func main() {
   fmt.Println(strconv.ParseUint("FF", 16, 8))
   // 255
}

func Atoi(s string) (i int, err error)
Atoi相當於ParseInt(s, 10, 0)

func FormatInt(i int64, base int) string
FormatUint將int型整數i轉換為字符串形式
base:進位制(2進制到36進制),大於10進制的數,返回值使用小寫字母‘a‘到‘z‘

package main

import (
   "fmt"
   "strconv"
)

func main() {
   i := int64(-2048)
   fmt.Println(strconv.FormatInt(i, 2))  // -100000000000
   fmt.Println(strconv.FormatInt(i, 8))  // -4000
   fmt.Println(strconv.FormatInt(i, 10)) // -2048
   fmt.Println(strconv.FormatInt(i, 16)) // -800
   fmt.Println(strconv.FormatInt(i, 36)) // -1kw
}

func Itoa(i int) string
Itoa相當於FormatInt(i, 10)

func AppendInt(dst []byte, i int64, base int) []byte
AppendInt將int型整數i轉換為字符串形式,並追加到dst的尾部
i:要轉換的字符串,base:進位制,返回追加後的[]byte。

func AppendUint(dst []byte, i uint64, base int) []byte
AppendUint將uint型整數i轉換為字符串形式,並追加到dst的尾部
i:要轉換的字符串,base:進位制,返回追加後的[]byte。

4、字符串與浮點型轉換

func ParseFloat(s string, bitSize int) (f float64, err error)
ParseFloat將字符串轉換為浮點數,
s:要轉換的字符串
bitSize:指定浮點類型(32:float32、64:float64)
如果s是合法的格式,而且接近一個浮點值,則返回浮點數的四舍五入值(依據 IEEE754 的四舍五入標準);如果s不是合法的格式,則返回“語法錯誤”;如果轉換結果超出bitSize範圍,則返回“超出範圍”。

package main

import (
   "fmt"
   "strconv"
)

func main() {
   s := "0.12345678901234567890"
   f, err := strconv.ParseFloat(s, 32)
   fmt.Println(f, err)          // 0.12345679104328156
   fmt.Println(float32(f), err) // 0.12345679
   f, err = strconv.ParseFloat(s, 64)
   fmt.Println(f, err) // 0.12345678901234568
}

func FormatFloat(f float64, fmt byte, prec, bitSize int) string
FormatFloat 將浮點數 f 轉換為字符串值
f:要轉換的浮點數
fmt:格式標記(b、e、E、f、g、G)
prec:精度(數字部分的長度,不包括指數部分)
bitSize:指定浮點類型(32:float32、64:float64)
格式標記:
‘b‘ (-ddddp±ddd,二進制指數)
‘e‘ (-d.dddde±dd,十進制指數)
‘E‘ (-d.ddddE±dd,十進制指數)
‘f‘ (-ddd.dddd,沒有指數)
‘g‘ (‘e‘:大指數,‘f‘:其它情況)
‘G‘ (‘E‘:大指數,‘f‘:其它情況)
如果格式標記為 ‘e‘,‘E‘和‘f‘,則prec表示小數點後的數字位數
如果格式標記為 ‘g‘,‘G‘,則prec表示總的數字位數(整數部分+小數部分)

package main

import (
   "fmt"
   "strconv"
)

func main() {
   f := 100.12345678901234567890123456789
   fmt.Println(strconv.FormatFloat(f, ‘b‘, 5, 32))
   // 13123382p-17
   fmt.Println(strconv.FormatFloat(f, ‘e‘, 5, 32))
   // 1.00123e+02
   fmt.Println(strconv.FormatFloat(f, ‘E‘, 5, 32))
   // 1.00123E+02
   fmt.Println(strconv.FormatFloat(f, ‘f‘, 5, 32))
   // 100.12346
   fmt.Println(strconv.FormatFloat(f, ‘g‘, 5, 32))
   // 100.12
   fmt.Println(strconv.FormatFloat(f, ‘G‘, 5, 32))
   // 100.12
   fmt.Println(strconv.FormatFloat(f, ‘b‘, 30, 32))
   // 13123382p-17
   fmt.Println(strconv.FormatFloat(f, ‘e‘, 30, 32))
   // 1.001234588623046875000000000000e+02
   fmt.Println(strconv.FormatFloat(f, ‘E‘, 30, 32))
   // 1.001234588623046875000000000000E+02
   fmt.Println(strconv.FormatFloat(f, ‘f‘, 30, 32))
   // 100.123458862304687500000000000000
   fmt.Println(strconv.FormatFloat(f, ‘g‘, 30, 32))
   // 100.1234588623046875
   fmt.Println(strconv.FormatFloat(f, ‘G‘, 30, 32))
   // 100.1234588623046875
}

func AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int) []byte
AppendFloat將浮點數f轉換為字符串值,並將轉換結果追加到dst的尾部,返回追加後的[]byte

package main

import (
   "fmt"
   "strconv"
)

func main() {
   f := 100.12345678901234567890123456789
   b := make([]byte, 0)
   b = strconv.AppendFloat(b, f, ‘f‘, 5, 32)
   b = append(b, " "...)
   b = strconv.AppendFloat(b, f, ‘e‘, 5, 32)
   fmt.Printf("%s", b) // 100.12346 1.00123e+0
}

5、特殊字符處理函數

func Quote(s string) string
Quote將字符串s轉換為“雙引號”引起來的字符串
其中的特殊字符將被轉換為“轉義字符”,“不可顯示的字符”將被轉換為“轉義字符”。

package main

import (
   "fmt"
   "strconv"
)

func main() {
   fmt.Println(strconv.Quote(`/home/user`)) // "/home/user"
}

func AppendQuote(dst []byte, s string) []byte
AppendQuote將字符串s轉換為“雙引號”引起來的字符串,並將結果追加到dst的尾部,返回追加後的[]byte。其中的特殊字符將被轉換為“轉義字符”。

func QuoteToASCII(s string) string
QuoteToASCII將字符串s轉換為“雙引號”引起來的ASCII字符串,“非ASCII字符”和“特殊字符”將被轉換為“轉義字符”。

func AppendQuoteToASCII(dst []byte, s string) []byte
AppendQuoteToASCII將字符串s轉換為“雙引號”引起來的 ASCII 字符串,並將結果追加到dst的尾部,返回追加後的[]byte,“非 ASCII 字符”和“特殊字符”將被轉換為“轉義字符”。

func QuoteRune(r rune) string
QuoteRune將Unicode字符轉換為“單引號”引起來的字符串,“特殊字符”將被轉換為“轉義字符”。

func AppendQuoteRune(dst []byte, r rune) []byte
AppendQuoteRune將Unicode字符轉換為“單引號”引起來的字符串,並將結果追加到dst的尾部,返回追加後的[]byte。“特殊字符”將被轉換為“轉義字符”。

func QuoteRuneToASCII(r rune) string
QuoteRuneToASCII將Unicode字符轉換為“單引號”引起來的ASCII字符串,“非 ASCII字符”和“特殊字符”將被轉換為“轉義字符”。

func AppendQuoteRuneToASCII(dst []byte, r rune) []byte
AppendQuoteRune將Unicode字符轉換為“單引號”引起來的ASCII字符串,並將結果追加到dst的尾部,返回追加後的[]byte,“非 ASCII 字符”和“特殊字符”將被轉換為“轉義字符”。

func CanBackquote(s string) bool
CanBackquote判斷字符串s是否可以表示為一個單行的“反引號”字符串,字符串中不能含有控制字符(除了\t)和“反引號”字符,否則返回false。

func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error)
UnquoteChar 將 s 中的第一個字符“取消轉義”並解碼
s:轉義後的字符串
quote:字符串使用的“引號符”(用於對引號符“取消轉義”)
value: 解碼後的字符
multibyte:value是否為多字節字符
tail: 字符串s除去value後的剩余部分
error:返回s中是否存在語法錯誤
參數 quote 為“引號符”
如果設置為單引號,則 s 中允許出現 \‘ 字符,不允許出現單獨的 ‘ 字符
如果設置為雙引號,則 s 中允許出現 \" 字符,不允許出現單獨的 " 字符
如果設置為 0,則不允許出現 \‘ 或 \" 字符,可以出現單獨的 ‘ 或 " 字符

func Unquote(s string) (t string, err error)
Unquote 將“帶引號的字符串” s 轉換為常規的字符串(不帶引號和轉義字符)
s可以是“單引號”、“雙引號”或“反引號”引起來的字符串(包括引號本身)
如果s是單引號引起來的字符串,則返回該該字符串代表的字符

func IsPrint(r rune) bool
IsPrint判斷Unicode字符r是否是一個可顯示的字符,空格可以顯示,而\t則不能顯示。

四、time

1、time簡介

time包提供顯示和計算時間用的函數。Go中時間處理依賴的數據類型:?time.Time,?time.Month,?time.Weekday,?time.Duration,?time.Location。

2、time.Time時間點

time.Time?代表一個納秒精度的時間點,定義如下:

type Time struct {
   sec int64 // 從1年1月1日 00:00:00 UTC 至今過去的秒數
   nsec int32 // 最近一秒到下一秒過去的納秒數
   loc *Location // 時區
}

time.Time使用示例:

package main

import (
   "fmt"
   "time"
)

func main() {
   var t time.Time // 定義 time.Time 類型變量
   t = time.Now()  // 獲取當前時間
   fmt.Printf("時間: %v, 時區:  %v,  時間類型: %T\n", t, t.Location(), t)

   // time.UTC() time 返回UTC 時區的時間
   fmt.Printf("時間: %v, 時區:  %v,  時間類型: %T\n", t.UTC(), t.UTC().Location(), t)

}
// output
// 時間: 2018-10-06 15:31:06.730640277 +0800 CST m=+0.000273307, 時區:  Local,  時間類型: time.Time
// 時間: 2018-10-06 07:31:06.730640277 +0000 UTC, 時區:  UTC,  時間類型: time.Time

時間獲取的接口:

func Now() Time {} // 當前本地時間
func Unix(sec int64, nsec int64) Time {} // 根據時間戳返回本地時間
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time {} // 返回指定時間
時間顯示的接口:
func (t Time) UTC() Time {} // 獲取指定時間在UTC 時區的時間表示
func (t Time) Local() Time {} // 以本地時區表示
func (t Time) In(loc *Location) Time {} // 時間在指定時區的表示
func (t Time) Format(layout string) string {} // 按指定格式顯示時間

日期信息獲取的接口:

func (t Time) Date() (year int, month Month, day int) {} // 返回時間的日期信息
func (t Time) Year() int {} // 返回年
func (t Time) Month() Month {} // 月
func (t Time) Day() int {} // 日
func (t Time) Weekday() Weekday {} // 星期
func (t Time) ISOWeek() (year, week int) {} // 返回年,星期範圍編號
func (t Time) Clock() (hour, min, sec int) {} // 返回時間的時分秒
func (t Time) Hour() int {} // 返回小時
func (t Time) Minute() int {} // 分鐘
func (t Time) Second() int {} // 秒
func (t Time) Nanosecond() int {} // 納秒
func (t Time) YearDay() int {} // 一年中對應的天
func (t Time) Location() *Location {} // 時間的時區
func (t Time) Zone() (name string, offset int) {} // 時間所在時區的規範名和想對UTC 時間偏移量
func (t Time) Unix() int64 {} // 時間轉為時間戳
func (t Time) UnixNano() int64 {} // 時間轉為時間戳(納秒)

時間比較與計算的接口:

func (t Time) IsZero() bool {} // 是否是零時時間
func (t Time) After(u Time) bool {} // 時間在u 之前
func (t Time) Before(u Time) bool {} // 時間在u 之後
func (t Time) Equal(u Time) bool {} // 時間與u 相同
func (t Time) Add(d Duration) Time {} // 返回t +d 的時間點
func (t Time) Sub(u Time) Duration {} // 返回 t-u
func (t Time) AddDate(years int, months int, days int) Time {} //返回增加了給出的年份、月份和天數的時間點Time

時間序列化的接口:

func (t Time) MarshalBinary() ([]byte, error) {} // 時間序列化
func (t Time) UnmarshalBinary(data []byte) error {} // 反序列化
func (t Time) MarshalJSON() ([]byte, error) {} // 時間序列化
func (t Time) MarshalText() ([]byte, error) {} // 時間序列化
func (t Time) GobEncode() ([]byte, error) {} // 時間序列化
func (t Time) GobDecode() ([]byte, error) {} // 時間序列化

3、?time.Month月份

time.Month?代表一年中的某個月

type Month int
const (
   January Month = 1 + iota
   February
   March
   April
   May
   June
   July
   August
   September
   October
   November
   December
)

4、time.Weekday星期

time.Weekday?代表一周的周幾

type Weekday int
const (
   Sunday Weekday = iota
   Monday
   Tuesday
   Wednesday
   Thursday
   Friday
   Saturday
)

5、time.Duration時間段

time.Duration?類型代表兩個時間點之間經過的納秒數,可表示的最長時間段約為290年。

type Duration int64
const (
   Nanosecond  Duration = 1
   Microsecond          = 1000 * Nanosecond
   Millisecond          = 1000 * Microsecond
   Second               = 1000 * Millisecond
   Minute               = 60 * Second
   Hour                 = 60 * Minute
)

輸出和改變時間段單位的接口:

func (d Duration) String() string // 格式化輸出 Duration
func (d Duration) Nanoseconds() int64 // 將時間段表示為納秒
func (d Duration) Seconds() float64 // 將時間段表示為秒
func (d Duration) Minutes() float64 // 將時間段表示為分鐘
func (d Duration) Hours() float64 // 將時間段表示為小時

6、time.Locations時區

Location代表一個地點,以及該地點所在的時區信息。北京時間可以使用?Asia/Shanghai。

type Location struct {
   name string
   zone []zone
   tx   []zoneTrans
   cacheStart int64
   cacheEnd   int64
   cacheZone  *zone
}
var UTC *Location = &utcLoc
var Local *Location = &localLoc

時區導出的接口:

func (l *Location) String() string // 輸出時區名
func FixedZone(name string, offset int) *Location // FixedZone 使用給定的地點名name和時間偏移量offset(單位秒)創建並返回一個Location
func LoadLocation(name string) (*Location, error) // LoadLocation 使用給定的名字創建Location

7、其它接口

func Sleep(d Duration)?
阻塞當前go協程至少d代表的時間段。d<=0時,Sleep會立刻返回。

五、errors

1、errors簡介

Go語言使用error類型來返回函數執行過程中遇到的錯誤,如果返回error值為nil,則表示未遇到錯誤,否則error會返回一個字符串。error是一個預定義標識符,代表一個Go語言內建的接口類型,任何自定義的錯誤類型都要實現Error接口函數。

type error interface {
   Error() string
}

2、error常用接口

func New(text string) error
將字符串text包裝成一個error對象返回?

import (
   "errors"
   "fmt"
)

var ErrUnexpectedEOF = errors.New("unexpected EOF")

func main() {
   fmt.Println(ErrUnexpectedEOF)
}

3、error定制

Go語言工程開發中,通常需要根據使用場景定制不同的error類型,此時需要實現Error接口。

package errors_test

import (
   "fmt"
   "time"
)

// MyError is an error implementation that includes a time and message.
type MyError struct {
   When time.Time
   What string
}

func (e MyError) Error() string {
   return fmt.Sprintf("%v: %v", e.When, e.What)
}

func oops() error {
   return MyError{
      time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),
      "the file system has gone away",
   }
}

func Example() {
   if err := oops(); err != nil {
      fmt.Println(err)
   }
   // Output: 1989-03-15 22:30:00 +0000 UTC: the file system has gone away
}

Go語言開發(十一)、Go語言常用標準庫一