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

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

after 更新 use har 相等 文件的 環境變量 its 內核

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

一、os

1、os簡介

os 包提供了不依賴平臺的操作系統函數接口,設計像Unix風格,但錯誤處理是go風格,當os包使用時,如果失敗後返回錯誤類型而不是錯誤數量。

2、os常用接口

func Hostname() (name string, err error) // Hostname返回內核提供的主機名
func Environ() []string // Environ返回表示環境變量的格式為”key=value”的字符串的切片拷貝
func Getenv(key string) string //  Getenv檢索並返回名為key的環境變量的值
func Getpid() int // Getpid返回調用者所在進程的進程ID
func Exit(code int) // Exit讓當前程序以給出的狀態碼code退出。一般來說,狀態碼0表示成功,非0表示出錯。程序會立刻終止,defer的函數不會被執行
func Stat(name string) (fi FileInfo, err error) // 獲取文件信息
func Getwd() (dir string, err error) // Getwd返回一個對應當前工作目錄的根路徑
func Mkdir(name string, perm FileMode) error // 使用指定的權限和名稱創建一個目錄
func MkdirAll(path string, perm FileMode) error // 使用指定的權限和名稱創建一個目錄,包括任何必要的上級目錄,並返回nil,否則返回錯誤
func Remove(name string) error // 刪除name指定的文件或目錄
func TempDir() string // 返回一個用於保管臨時文件的默認目錄
var Args []string // os.Args返回一個字符串數組,其中第一個參數就是執行文件本身

os示例:

package main

import (
   "fmt"
   "os"
)

func main() {
   // 預定義變量, 保存命令行參數
   fmt.Println(os.Args)
   // 獲取host name
   fmt.Println(os.Hostname())
   fmt.Println(os.Getpid())
   // 獲取全部環境變量
   env := os.Environ()
   for k, v := range env {
      fmt.Println(k, v)
   }
   // 終止程序
   // os.Exit(1)
   // 獲取一條環境變量
   fmt.Println(os.Getenv("PATH"))
   // 獲取當前目錄
   dir, err := os.Getwd()
   fmt.Println(dir, err)
   // 創建目錄
   err = os.Mkdir(dir+"/new_file", 0755)
   fmt.Println(err)
   // 創建目錄
   err = os.MkdirAll(dir+"/new", 0755)
   fmt.Println(err)
   // 刪除目錄
   err = os.Remove(dir + "/new_file")
   err = os.Remove(dir + "/new")
   fmt.Println(err)
   // 創建臨時目錄
   tmp_dir := os.TempDir()
   fmt.Println(tmp_dir)
}

3、File結構體

func Create(name string) (file *File, err error) // Create采用模式0666創建一個名為name的文件,如果文件已存在會截斷(為空文件)
func Open(name string) (file *File, err error) // Open打開一個文件用於讀取。如果操作成功,返回的文件對象的方法可用於讀取數據
func (f *File) Stat() (fi FileInfo, err error) // Stat返回描述文件f的FileInfo類型值
func (f *File) Readdir(n int) (fi []FileInfo, err error) //  Readdir讀取目錄f的內容,返回一個有n個成員的[]FileInfo
func (f *File) Read(b []byte) (n int, err error) // Read方法從f中讀取最多len(b)字節數據並寫入b
func (f *File) WriteString(s string) (ret int, err error) // 向文件中寫入字符串
func (f *File) Sync() (err error) // Sync遞交文件的當前內容進行穩定的存儲
func (f *File) Close() error // Close關閉文件f,使文件不能用於讀寫

File示例:

package main

import (
   "fmt"
   "os"
   "time"
)

func main() {
   // 獲取當前目錄
   dir, err := os.Getwd()
   fmt.Println(dir, err)
   file := dir + "/new"
   var fh *os.File
   fi, _ := os.Stat(file)
   if fi == nil {
      fh, _ = os.Create(file) // 文件不存在就創建
   } else {
      fh, _ = os.OpenFile(file, os.O_RDWR, 0666) // 文件存在就打開
   }
   w := []byte("hello go language" + time.Now().String())
   n, err := fh.Write(w)
   fmt.Println(n, err)
   // 設置下次讀寫位置
   ret, err := fh.Seek(0, 0)
   fmt.Printf("%s %v %v\n","當前文件指針位置", ret, err)
   b := make([]byte, 128)
   n, err = fh.Read(b)
   fmt.Printf("%d %v %s\n",n, err, string(b))
   fh.Close()
}

4、FileInfo結構體

FileInfo用來描述一個文件對象。

type FileInfo interface {
   Name() string       // base name of the file
   Size() int64        // length in bytes for regular files; system-dependent for others
   Mode() FileMode     // file mode bits
   ModTime() time.Time // modification time
   IsDir() bool        // abbreviation for Mode().IsDir()
   Sys() interface{}   // underlying data source (can return nil)
}

func Stat(name string) (fi FileInfo, err error)
Stat返回描述文件的FileInfo。如果指定的文件對象是一個符號鏈接,返回的FileInfo描述該符號鏈接指向的文件的信息,本函數會嘗試跳轉該鏈接
func Lstat(name string) (fi FileInfo, err error)
Lstat返回描述文件對象的FileInfo。如果指定的文件對象是一個符號鏈接,返回的FileInfo描述該符號鏈接的信息,本函數不會試圖跳轉該鏈接。

package main

import (
   "os"
)

func main() {
   file := "/home/user/hello.go"
   fi, _ := os.Stat(file)
   if fi == nil {
      fh, _ := os.Create(file) // 文件不存在就創建
      fh.Write([]byte("package main\nfunc main(){\n}\n"))
   } else {
      fh, _ := os.OpenFile(file, os.O_RDWR, 0666) // 文件存在就打開
      fh.Write([]byte("package main\nfunc main(){\n}\n"))
   }
}

二、bufio

1、bufio簡介

bufio模塊通過對io模塊的封裝,提供了數據緩沖功能,能夠一定程度減少大塊數據讀寫帶來的開銷。
在bufio各個組件內部都維護了一個緩沖區,數據讀寫操作都直接通過緩存區進行。當發起一次讀寫操作時,會首先嘗試從緩沖區獲取數據;只有當緩沖區沒有數據時,才會從數據源獲取數據更新緩沖。

2、Reader

type Reader struct {
   buf          []byte
   rd           io.Reader // reader provided by the client
   r, w         int       // buf read and write positions
   err          error
   lastByte     int
   lastRuneSize int
}

可以通過NewReader函數創建bufio.Reader對象,函數接收一個io.Reader作為參數。因此,bufio.Reader不能直接使用,需要綁定到某個io.Reader上。

func NewReader(rd io.Reader) *Reader
func NewReaderSize(rd io.Reader, size int) *Reader // 可以配置緩沖區的大小

相較於io.Reader,bufio.Reader提供了很多實用的方法,能夠更有效的對數據進行讀取。bufio.Reader能夠對Reader進行細粒度的操作:
A、Read,讀取n個byte數據
B、Discard,丟棄接下來n個byte數據
C、Peek,獲取當前緩沖區內接下來的n個byte,但不移動指針
D、Reset,清空整個緩沖區

func (b *Reader) Read(p []byte) (n int, err error)
func (b *Reader) Discard(n int) (discarded int, err error)
func (b *Reader) Peek(n int) ([]byte, error)
func (b *Reader) Reset(r io.Reader)

bufio.Reader還提供了多個更高抽象層次的方法對數據進行簡單的結構化讀取,如下:
A、ReadByte,讀取一個byte
B、ReadRune,讀取一個utf-8字符
C、ReadLine,讀取一行數據,由’\n’分隔
D、ReadBytes,讀取一個byte列表
E、ReadString,讀取一個字符串

func (b *Reader) ReadByte() (byte, error)
func (b *Reader) ReadRune() (r rune, size int, err error)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
func (b *Reader) ReadBytes(delim byte) ([]byte, error)
func (b *Reader) ReadString(delim byte) (string, error)

bufio.Reader使用示例:

package main

import (
   "strings"
   "fmt"
   "bufio"
)

func main() {
   r := strings.NewReader("hello world !")
   reader := bufio.NewReader(r)

   bytes, _ := reader.Peek(5)
   fmt.Printf("%s\n",bytes)
   n, _ := reader.Read(bytes)
   fmt.Println(n)
   reader.Discard(1)

   for {
      str, err := reader.ReadString(byte(‘ ‘))
      fmt.Println(str)
      if err != nil {
         return
      }
   }
}
// output
// hello
// 5
// world
// !

3、Writer

type Writer struct {
   err error
   buf []byte
   n   int
   wr  io.Writer
}
func NewWriter(w io.Writer) *Writer
func NewWriterSize(w io.Writer, size int) *Writer

創建Writer對象的接口

func (b *Writer) Write(p []byte) (nn int, err error) // 寫入n byte數據
func (b *Writer) Reset(w io.Writer) // 重置當前緩沖區
func (b *Writer) Flush() error // 清空當前緩沖區,將數據寫入輸出
func (b *Writer) WriteByte(c byte) error  // 寫入一個字節
func (b *Writer) WriteRune(r rune) (size int, err error) // 寫入一個字符
func (b *Writer) WriteString(s string) (int, error) // 寫入一個字符串
func (b *Writer) Available() int // 緩存中有多少字節空間可用
func (b *Writer) Buffered() int // 當前緩存已經寫入了多少字節
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) // 實現io.ReaderFrom

4、ReadWriter

type ReadWriter struct {
   *Reader
   *Writer
}

ReadWriter實現了io.ReadWriter。
func NewReadWriter(r *Reader, w *Writer) *ReadWriter
ReadWriter對象創建

5、Scanner

type Scanner struct {
   r            io.Reader // The reader provided by the client.
   split        SplitFunc // The function to split the tokens.
   maxTokenSize int       // Maximum size of a token; modified by tests.
   token        []byte    // Last token returned by split.
   buf          []byte    // Buffer used as argument to split.
   start        int       // First non-processed byte in buf.
   end          int       // End of data in buf.
   err          error     // Sticky error.
   empties      int       // Count of successive empty tokens.
   scanCalled   bool      // Scan has been called; buffer is in use.
   done         bool      // Scan has finished.
}

工程開發中推薦使用Scanner對數據進行讀取,而非直接使用Reader類。Scanner可以通過splitFunc將輸入數據拆分為多個token,然後依次進行讀取。
func NewScanner(r io.Reader) *Scanner
創建scanner對象
func (s *Scanner) Split(split SplitFunc)
設置scanner的分割函數。
在使用scanner前還需要設置splitFunc(默認為ScanLines),splitFunc用於將輸入數據拆分為多個token。bufio模塊提供了幾個默認splitFunc,能夠滿足大部分場景的需求,包括:
A、ScanBytes,按照byte進行拆分
B、ScanLines,按照行(“\n”)進行拆分
C、ScanRunes,按照utf-8字符進行拆分
D、ScanWords,按照單詞(” “)進行拆分
通過Scanner的Split方法,可以為Scanner指定splitFunc。使用方法如下:

scanner := bufio.NewScanner(os.StdIn)
scanner.split(bufio.ScanWords)

設置分割方式
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
函數接收兩個參數,第一個參數是輸入數據,第二個參數是一個標識位,用於標識當前數據是否為結束。函數返回三個參數,第一個是本次split操作的指針偏移;第二個是當前讀取到的token;第三個是返回的錯誤信息。

func (s *Scanner) Scan() bool
func (s *Scanner) Text() string
func (s *Scanner) Bytes() []byte

在完成Scanner初始化後,通過Scan方法可以在輸入中向前讀取一個token,讀取成功返回True;使用Text和Bytes方法獲取token,Text返回一個字符串,Bytes返回字節數組。
Scanner使用示例:

package main

import (
   "strings"
   "fmt"
   "bufio"
)

func main() {
   scanner := bufio.NewScanner(strings.NewReader("hello world !"))
   scanner.Split(bufio.ScanWords)
   for scanner.Scan() {
      fmt.Println(scanner.Text())
   }
}
// output
// hello
// world
// !

三、ioutil

1、ioutil簡介

ioutil提供了對io包的封裝函數。

2、ReadAll

func ReadAll(r io.Reader) ([]byte, error)
ReadAll讀取r中的所有數據
返回讀取的數據和讀取過程中遇到的任何錯誤。如果讀取成功,則err返回nil,而不是EOF。

s := strings.NewReader("Hello World!")
ra, _ := ioutil.ReadAll(s)
fmt.Printf("%s", ra)
// Hello World!

func ReadFile(filename string) ([]byte, error)
ReadFile讀取文件中的所有數據,返回讀取的數據和讀取過程中遇到的任何錯誤。如果讀取成功,則err返回nil,而不是EOF。

content, err := ioutil.ReadFile("/home/user/hello.txt")
if err != nil {
   log.Fatal(err)
}

fmt.Printf("File contents: %s", content)

3、WriteFile

func WriteFile(filename string, data []byte, perm os.FileMode) error
WriteFile向文件filename中寫入數據data,如果文件不存在,則以perm權限創建該文件;如果文件存在,則先清空文件,然後再寫入。返回寫入過程中遇到的任何錯誤。

filename := "/home/user/hello.txt"
data := []byte("Hello World!")
ioutil.WriteFile(filename, data, os.ModeAppend)
contents, _ := ioutil.ReadFile(filename)
fmt.Printf("%s", contents)
// Hello World!

4、ReadDir

func ReadDir(dirname string) ([]os.FileInfo, error)
ReadDir讀取目錄dirmane中的所有目錄和文件(不包括子目錄)
返回讀取到的文件的信息列表和讀取過程中遇到的任何錯誤,返回的文件列表是經過排序的。

rd, err := ioutil.ReadDir("/home/user")
for _, fi := range rd {
   fmt.Println("")
   fmt.Println(fi.Name())
   fmt.Println(fi.IsDir())
   fmt.Println(fi.Size())
   fmt.Println(fi.ModTime())
   fmt.Println(fi.Mode())
}
fmt.Println("")
fmt.Println(err)

5、TempDir

func TempDir(dir, prefix string) (name string, err error)
TempDir功能同TempFile,只不過創建的是目錄,返回值也只返目錄的完整路徑。

content := []byte("temporary file‘s content")
dir, err := ioutil.TempDir("/home/user", "example")
if err != nil {
   log.Fatal(err)
}

defer os.RemoveAll(dir) // clean up

tmpfn := filepath.Join(dir, "tmpfile")
if err := ioutil.WriteFile(tmpfn, content, 0666); err != nil {
   log.Fatal(err)
}

6、TempFile

func TempFile(dir, prefix string) (f *os.File, err error)
TempFile在目錄dir中創建一個臨時文件並將其打開,文件名以prefix為前綴
返回創建的文件的對象和創建過程中遇到的任何錯誤
如果dir為空,則在系統的臨時目錄中創建臨時文件
如果環境變量中沒有設置系統臨時目錄,則在/tmp中創建臨時文件
調用者可以通過f.Name()方法獲取臨時文件的完整路徑
調用TempFile所創建的臨時文件,應該由調用者自己移除

content := []byte("temporary file‘s content")
tmpfile, err := ioutil.TempFile("/home/user", "example")
if err != nil {
   log.Fatal(err)
}

defer os.Remove(tmpfile.Name()) // clean up

if _, err := tmpfile.Write(content); err != nil {
   log.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
   log.Fatal(err)
}

7、Discard

var Discard io.Writer = devNull(0)
Discard是一個io.Writer,對它進行的任何Write調用都將無條件成功。devNull優化的實現ReadFrom,因此io.Copy到ioutil.Discard避免不必要的工作,因此其一定會成功。但是ioutil.Discard不記錄copy得到的數值。

a := strings.NewReader("hello")
p := make([]byte, 20)
io.Copy(ioutil.Discard, a)
ioutil.Discard.Write(p)
fmt.Println(p)
//[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

8、NopCloser

func NopCloser(r io.Reader) io.ReadCloser
ReadCloser接口組合了基本的Read和Close方法。NopCloser將提供的 Reader r用空操作Close方法包裝後作為ReadCloser返回。

s := strings.NewReader("hello world!")
r := ioutil.NopCloser(s)
r.Close()
p := make([]byte, 10)
r.Read(p)
fmt.Println(string(p))
//hello worl

四、bytes

1、bytes簡介

bytes包提供了對字節切片進行讀寫操作的一系列函數。?字節切片處理的函數比較多,分為基本處理函數、比較函數、後綴檢查函數、索引函數、分割函數、大小寫處理函數和子切片處理函數等。
strings與bytes的函數接口功能基本一致。

2、基本函數

func Contains(b,subslice []bytes) bool
檢查字節切片b是否包含子切片subslice,如果包含返回true,否則返回false。
func Count(s,sep []byte) int
計算字節切片sep在字節切片s中非重疊顯示的個數。
func Repeat(b[]byte,count int) []byte
把切片b復制count個,然後合成一個新的字節切片返回。
func Replace(s,old,new []byte,n int) []byte
返回字節切片s的一個副本,並把前n個不重疊的子切片old替換為new;如果n小於0,則不限制替換的數量。參數n為替換的次數。

func Runes(s []byte) []rune
把s轉換為UTF-8編碼的字節序列,並返回對應的Unicode切片
func Join(s [][]byte,sep[]byte) []byte
用字節切片sep把s中的每個字節切片連成一個字節切片並返回。
基本函數示例:

package main

import (
   "fmt"
   "bytes"
)

func main() {
   //Contains
   b := []byte("hello") //字符串強轉為byte切片
   sublice1 := []byte("h")
   sublice2 := []byte("l")
   fmt.Println(bytes.Contains(b,sublice1))// true
   fmt.Println(bytes.Contains(b,sublice2))// false

   //Count
   s := []byte("hello world")
   sep1 := []byte("ll")
   sep2 := []byte("l")
   sep3 := []byte("o")
   fmt.Println(bytes.Count(s,sep1))// 1
   fmt.Println(bytes.Count(s,sep2))// 3
   fmt.Println(bytes.Count(s,sep3))// 2

   //Repeat
   b = []byte("hello world")
   fmt.Println(string(bytes.Repeat(b,1)))// hello world
   fmt.Println(string(bytes.Repeat(b,2)))// hello worldhello world

   //Replace
   s = []byte("hello,world")
   old := []byte("o")
   news := []byte("ee")
   fmt.Println(string(bytes.Replace(s,old,news,0)))//hello,world
   fmt.Println(string(bytes.Replace(s,old,news,1)))//hellee,world
   fmt.Println(string(bytes.Replace(s,old,news,2)))//hellee,weerld
   fmt.Println(string(bytes.Replace(s,old,news,-1)))//hellee,weerld

   //Runes
   s = []byte("你好世界")
   r := bytes.Runes(s)
   fmt.Println("轉換前字符串的長度: ",len(s))//12
   fmt.Println("轉換後字符串的長度: ",len(r))//4

   //Join
   ss := [][]byte{[]byte("你好"),[]byte("世界")}
   sep4 := []byte(",")
   fmt.Println(string(bytes.Join(ss,sep4)))//你好,世界
   sep5 := []byte("#")
   fmt.Println(string(bytes.Join(ss,sep5)))//你好#世界
}

3、比較函數

func Compare(a,b[]byte) int
根據字節的值比較字節切片a和b的大小,如果a=b,返回0,如果a>b返回1,如果a小於b返回-1。
func Equal(a,b[]byte) bool
比較2個字節切片是否相等,如果參數為nil,則等同於空的字節切片,如果a=b,則返回true,否則返回false.區分大小寫。
func EqualFold(s,t[]byte) bool
把s和t轉換成UTF-8字符串進行比較,並且忽略大小寫,如果s=t,返回true,否則,返回false。
比較函數示例:

package main

import (
   "fmt"
   "bytes"
)

func main() {
   //Compare
   a := []byte("hello,go")
   b := []byte("hello,world")
   fmt.Println(bytes.Compare(a,b))//-1
   b =[]byte("hello,c")
   fmt.Println(bytes.Compare(a,b))//1
   b =[]byte("hello,World")
   fmt.Println(bytes.Compare(a,b))//1  小寫字母大於大寫字母
   b =[]byte("b")
   fmt.Println(bytes.Compare(a,b))//-1 從第一個字節開始比較,如果相同再比較長度

   //Equal
   a = []byte("abc")
   b = []byte("ABC")
   fmt.Println(bytes.Equal(a,b))//false
   fmt.Println(bytes.Equal(a,nil))//false
   b = []byte("abc")
   fmt.Println(bytes.Equal(a,b))//true

   //EqualFold
   a = []byte("abc")
   b = []byte("ABC")
   fmt.Println(bytes.EqualFold(a,b))//true
}

4、前後綴檢查

func HasPrefix(s,prefix[]byte) bool
檢查字節切片s的前綴是否為prefix,如果是返回true,如果不是返回false
func HashSuffix(s,suffix[]byte) bool
檢查字節切片s的後綴是否為suffix,如果是返回true,否則返回false。
前後綴檢查示例:

package main

import (
   "fmt"
   "bytes"
)

func main() {
   //HasPrefix
   s := []byte("test_Hello.txt")
   prefix := []byte("test")
   fmt.Println(bytes.HasPrefix(s,prefix))//true
   prefix = []byte("Test")
   fmt.Println(bytes.HasPrefix(s,prefix))//false

   //HashSuffix
   suffix := []byte("txt")
   fmt.Println(bytes.HasSuffix(s,suffix))//true
}

5、位置索引

字節切片位置索引函數共有8個,Index()、IndexAny()、IndexByte()、IndexFunc()、IndexRune()、LastIndex()、LastIndexAny()和LastIndexFunc()。
func Index(s,sep []byte) int
返回sep在s中第一次出現的位置索引(從0開始),如果sep中不在s中則返回-1
func IndexAny(s []byte,chars string) int
把s解析為UTF-8編碼的字節序列,返回chars中任何一個字符在s中第一次出現的索引位置;如果s中不包含chars中任何一個字符,則返回-1
func IndexByte(s[]byte,c byte) int
檢查字節c在s中第一次出現的位置索引;如果s中不包含c則返回-1
func IndexFunc(s[]byte,f func(r rune)bool) int
把s解析為UTF-8字節序列,並返回一個滿足f(c)=true的字符c的位置索引,如果沒有滿足則返回-1
func IndexRune(s[]byte,r rune) int
把s解析為UTF-8字節序列,並返回rune類型的字符r在s中的位置索引,如果s中不包含r則返回-1
func LastIndex(s,sep[]byte) int
返回sep在s中最後一次出現的位置索引,如果s中不包含sep,則返回-1
func LastIndexAny(s[]byte,chars string) int
把s解析為UTF-8字節序列,返回chars中任何一個字符在s中最後
出現的位置索引,如果chars為空或者s中不包含chars中的任意字符,則返回-1
func LastIndexFunc(s[]byte,f func(r rune)bool) int
把s解析成UTF-8字節序列,返回滿足f(s)=true的字符c在s中最後
一次出現的位置索引,如果沒有找到則返回-1
位置索引示例:

package main

import (
   "fmt"
   "bytes"
)

func main() {
   //Index
   a := []byte("hello,world")
   fmt.Println(bytes.Index(a,[]byte("o")))//4
   fmt.Println(bytes.Index(a,[]byte("ll")))//2
   fmt.Println(bytes.Index(a,[]byte("w")))//6

   //IndexAny
   fmt.Println(bytes.IndexAny(a,"h"))//0
   fmt.Println(bytes.IndexAny(a,"l"))//2

   //IndexByte
   s := []byte("hello,world")
   var ch byte = ‘w‘
   fmt.Println(bytes.IndexByte(s,ch))//6

   //IndexFunc,可以接收匿名函數
   fmt.Println(bytes.IndexFunc(s,func (a rune)bool{
      if a == ‘o‘{
         return true
      }else{
         return false
      }
   }))//4

   //IndexRune
   fmt.Println(bytes.IndexRune(s,‘e‘))//1
   fmt.Println(bytes.IndexRune(s,‘a‘))//-1

   //LastIndex
   fmt.Println(bytes.LastIndex(s,[]byte("g")))//-1
   fmt.Println(bytes.LastIndex(s,[]byte("e")))//1
   fmt.Println(bytes.LastIndex(s,[]byte("o")))//7

   //LastIndexAny
   fmt.Println(bytes.LastIndexAny(s,"world"))//10
   fmt.Println(bytes.LastIndexAny(s,"l"))//9
   fmt.Println(bytes.LastIndexAny(s,"d"))//10

   //LastIndexFunc
   fmt.Println(bytes.LastIndexFunc(s,func(r rune)bool{
      if r==‘d‘{
         return true
      }else {
         return false
      }
   }))//10
}

6、分割函數

字節切片分割函數共有6個,Fields(),FieldsFunc(),Split(),SplitN(),?
SplitAfter()和SplitAfterN()。
func Fields(s[]byte) [][]byte
把字節切片s按照一個或者連續多個空白字符分割成多個字節切片,如果s只包含空白字符則返回空字節切片,其中參數s準備分割的字節切片
func FieldsFunc(s []byte,f func(r rune)bool) [][]byte
把s解析為UTF-8字節序列,對於每個Unicode字符c,如果f(c)返回true就把c作為分割字符對s進行拆分。如果所有字符都滿足f(c)為true,則返回空切片
func Split(s,sep[]byte)[][]byte
把s用sep分割成多個字節切片並返回,如果sep為空,Split則把s切分成每個字節切片對應一個UTF-8字符,Split()等效於參數為n的splitN()函數。
func SplitAfter(s,sep[]byte)[][]byte
使用sep作為後綴把s切分成多個字節切片並返回。如果sep為空,則把s切分成每個字節切片對應一個UTF-8字符。
func SplitAfterN(s,sep[]byte,n int)[][]byte
用sep作為後綴把s切分成多個字節切片並返回。如果sep為空,則把s切分成每個字節切片對應一個UTF-8字符。參數n決定返回切片的長度:如果n>0,最多返回n個子字節切片,子切片可能包含未切分的字節序列;如果n=0,返回空切片;如果n< 0,返回所有子切片。
func SplitN(s,sep []byte,n int)[][]byte
把s用sep分割成多個字節切片並返回,如果sep為空,Split則把s切分成每個字節切片對應一個UTF-8字符,參數n決定返回長度,n>0最多返回n個子切片;n==0返回返回空切片,n小於0返回所有子切片。
分割函數示例:

package main

import (
   "fmt"
   "bytes"
)

func main() {
   //Fields ,返回的是2維切片
   s := []byte("hello world")
   for _,v := range bytes.Fields(s){
      //遍歷獲取1維切片,再強轉為字符串
      fmt.Print(string(v)+",") // hello,world,
   }
   fmt.Println()
   //FieldsFunc,返回是2維切片,接收匿名函數
   s = []byte("hello,world")
   for _,v := range bytes.FieldsFunc(s,func(r rune)bool{
      if r == ‘,‘{
         return true //按照空白字符分割
      }else{
         return false
      }
   }){
      fmt.Print(string(v)+",")// hello,world,
   }
   fmt.Println()
   //Split
   s = []byte("天山老妖")
   for _,v := range bytes.Split(s,[]byte("山")){
      fmt.Print(string(v)+",")//天,老妖,
   }
   fmt.Println()
   for _,v := range bytes.Split(s,nil){
      fmt.Print(string(v)+",")//天,山,老,妖,
   }
   fmt.Println()
   //SplitAfter
   for _,v := range bytes.SplitAfter(s,[]byte("山")){
      fmt.Print(string(v)+",")//天山,老妖,
   }
   fmt.Println()
   for _,v := range bytes.SplitAfter(s,nil){
      fmt.Print(string(v)+",")//天,山,老,妖,
   }
   fmt.Println()
   //SplitAfterN
   s = []byte("hello,world,hello,go")
   for _,v := range bytes.SplitAfterN(s,[]byte(","),0){
      fmt.Print(string(v)+",") //什麽都不輸出
   }
   fmt.Println()
   for _,v := range bytes.SplitAfterN(s,[]byte(","),4){
      fmt.Print(string(v))//hello,world,hello,go
   }
   fmt.Println()
   for _,v := range bytes.SplitAfterN(s,[]byte(""),-1){
      fmt.Print(string(v))//hello,world,hello,go
   }
   fmt.Println()
   //SplitN
   s = []byte("hello,world")
   for _,v := range bytes.SplitN(s,[]byte("he"),0){
      fmt.Print(string(v)+",") //
   }
   fmt.Println()
   for _,v := range bytes.SplitN(s,[]byte("o"),3){
      fmt.Print(string(v) + ",")//hell,,w,rld,
   }
   fmt.Println()
   for _,v := range bytes.SplitN(s,[]byte("ll"),-1){
      fmt.Print(string(v)+",")//he,o,world,
   }
}

7、大小寫處理函數

共有7個函數,Title(),ToTitle(),ToTitleSpecial(),ToLower(),ToLowerSpecial(),ToUpper()?和ToUpperSpecial()。
func Title(s[]byte) []byte
返回一個s的副本,把s中每個單詞的首字母改成Unicode字符大寫
func ToTitle(s []byte) []byte
返回s的一個副本,並把其中所有Unicode字符轉為大寫
func ToTitleSpecial(_case unicode.SpecialCase,s []byte) []byte
返回s的一個副本,並把其中所有Unicode字符根據_case指定的規則轉成大寫
func ToLower(s []byte)[]byte
返回s的一個副本,並把其中的所有Unicode字符轉為小寫
func ToLowerSpecial(_case unicode.SpecialCase, s []byte) []byte
返回s的一個副本,並把其中所有Unicode字符根據_case定的規則轉換成小寫
func ToUpper(s []byte) []byte
返回s的一個副本,並把其中所有Unicode字符都轉為大寫
func ToUpperSpecial(_case unicode.SpecialCase, s []byte) []byte
返回s的一個副本,並把其中所有Unicode字符都根據_case指定的規則轉成大寫
大小寫處理函數示例:

package main

import (
   "fmt"
   "bytes"
   "unicode"
)

func main() {
   s := []byte("hello,go")
   fmt.Println(string(bytes.Title(s)))// Hello,Go
   fmt.Println(string(bytes.ToTitle(s)))// HELLO,GO
   fmt.Println(string(bytes.ToTitleSpecial(unicode.AzeriCase,s)))// HELLO,GO
   s = []byte("Hello,Go")
   fmt.Println(string(bytes.ToLower(s)))// hello,go
   fmt.Println(string(bytes.ToLowerSpecial(unicode.AzeriCase,s)))// hello,go
   s = []byte("Hello,Go")
   fmt.Println(string(bytes.ToUpper(s)))// HELLO,GO
   fmt.Println(string(bytes.ToUpperSpecial(unicode.AzeriCase,s)))// HELLO,GO
}

8、子字節切片處理函數

子字節切片處理函數共有9個,Trim(),TrimFunc(),TrimLeft(),TrimLeftFunc(),TrimRight(),TrimRightFunc(),TrimSpace(),TrimPrefix()和TrimSuffix()。
func Trim(s []byte, cutset string) []byte
返回s的子字節切片,cutset中任意出現在s的首部和尾部的連續字符將被刪除。
func TrimFunc(s []byte, f func(r rune) bool) []byte
返回s的子字節切片,刪除s首部和尾部連接的滿足f(c)=true的字符c。
func TrimLeft(s []byte, cutset string) []byte
返回s的子字節切片,cutset中任意出現在s首部的連續字符被刪除。
func TrimLeftFunc(s []byte, f func(r rune) bool) []byte
返回s的一個子字節切片、刪除s首部連續滿足f(c)=true的字符c。
func TrimRight(s []byte, cutset string) []byte
返回s的子字節切片,cutset中任意出現在s尾部的連續字符被刪除。
func TrimRightFunc(s []byte, f func(r rune) bool) []byte
返回s的一個子字節切片、刪除s尾部連續滿足f(c)=true的字符c
func TrimSpace(s []byte) []byte
返回s的一個子字節切片,並刪除s中開始和結尾處的連續的Unicode空白字符。
func TrimPrefix(s, prefix []byte) []byte
返回s的一個子字節切片,並刪除前綴為prefix的部分
func TrimSuffix(s, suffix []byte) []byte
返回s的一個子字節切片,並刪除後綴為suffix的部分
子字節切片處理函數示例:

package main

import (
   "fmt"
   "bytes"
)

func main() {
   //Trim
   s := []byte(" Hello,Go  ")
   fmt.Println(string(bytes.Trim(s," ")))// Hello,Go

   //TrimFunc
   s = []byte("hello world")
   fmt.Println(string(bytes.TrimFunc(s,func(r rune)bool{
      if r==‘h‘ || r==‘d‘{
         return true
      }else{
         return false
      }
   }))) //ello worl

   s = []byte("hello,worldo")
   fmt.Println(string(bytes.TrimFunc(s,func(r rune)bool{
      if r==‘h‘ || r==‘o‘{
         return true
      }else{
         return false
      }
   }))) // ello,world

   s = []byte("hello,world")
   fmt.Println(string(bytes.TrimFunc(s,func(r rune)bool{
      if r==‘h‘ && r==‘o‘{
         return true
      }else{
         return false
      }
   }))) // hello,world

   //TrimLeft
   fmt.Println(string(bytes.TrimLeft(s,"h")))// ello,world
   fmt.Println(string(bytes.TrimLeft(s,"l")))// hello,world

   //TrimLeftFunc
   fmt.Println(string(bytes.TrimLeftFunc(s,func(r rune)bool{
      if r == ‘h‘ || r==‘l‘{
         return true
      }else{
         return false
      }

   }))) // ello,world

   //TrimRight
   fmt.Println(string(bytes.TrimRight(s,"d")))// hello,worl

   //TrimRightFunc
   fmt.Println(string(bytes.TrimRightFunc(s,func(r rune)bool{
      if r == ‘d‘{
         return true
      }else{
         return false
      }

   })))// hello,worl

   //TrimSpace
   s = []byte("  hello world   ")
   fmt.Println(string(bytes.TrimSpace(s)))// hello world

   //TrimPrefix
   s = []byte("test_Go")
   fmt.Println(string(bytes.TrimPrefix(s,[]byte("test_"))))// Go
   fmt.Println(string(bytes.TrimPrefix(s,[]byte("Test"))))// test_Go

   //TrimSuffix
   s = []byte("hello.go")
   fmt.Println(string(bytes.TrimSuffix(s,[]byte(".go"))))// hello
   fmt.Println(string(bytes.TrimSuffix(s,[]byte(".cpp"))))// hello.go
}

五、path

1、path簡介

path實現了對斜杠分隔的路徑進行操作的函數。
filepath包實現了兼容各操作系統的文件路徑操作函數

2、path常用接口

func IsAbs(path string) bool
判斷是否是一個絕對路徑
func Split(path string) (dir, file string)
將路徑分割為路徑和文件名
func Join(elem ...string) string
將多個字符串合並為一個路徑
func Ext(path string) string
返回路徑中擴展部分
func Base(path string) string
返回路徑的最後一個元素
func Dir(path string) string
返回路徑中目錄部分
func Clean(path string) string
返回同目錄的最短路徑
func Match(pattern, name string) (matched bool, err error)
正則是否匹配路徑(shell文件名匹配)

package main

import (
   "fmt"
   "path"
)

func main() {
   pt := "~/GoLang"

   // 判斷是否是一個絕對路徑
   is_abs := path.IsAbs(pt)
   fmt.Println(is_abs) // false

   // 將路徑分割為路徑和文件名
   pf := "/home/user/hello.go"
   dir, file := path.Split(pf)
   fmt.Println(dir, file) // /home/user/ hello.go

   // 將多個字符串合並為一個路徑
   dir_join := path.Join("usr", "local", "bin")
   fmt.Println(dir_join) // usr/local/bin

   // 返回路徑中擴展
   file_ext := path.Ext(pf)
   fmt.Println(file_ext) // .go

   // 返回路徑的最後一個元素
   dir_base := path.Base(pf)
   fmt.Println(dir_base) // hello.go

   // 返回路徑中目錄部分
   dir = path.Dir(pf)
   fmt.Println(dir) // /home/user

   // 返回同目錄的最短路徑
   dir_a := "/usr/../opt/../home/user"
   fmt.Println(path.Clean(dir_a)) // /home/user

   // 正則是否匹配路徑
   is_match, err := path.Match("*.xml", "a.xml")
   fmt.Println(is_match, err) // true <nil>
}

3、filepath常用接口

filepath.Separator
預定義變量,表示路徑分隔符 /
filepath.ListSeparator
預定義變量,表示環境變量分隔符 :
func Abs(path string) (string, error)
返回path相對當前路徑的絕對路徑
func Clean(path string) string
返path的最短路徑
func Rel(basepath, targpath string) (string, error)
返回targpath相對 basepath路徑
func EvalSymlinks(path string) (string, error)
返回軟鏈指向的路徑
func VolumeName(path string) string
返回路徑最前面的卷名
func ToSlash(path string) string
路徑分隔符替換為 /
func FromSlash(path string) string
/ 替換為路徑分隔符
func SplitList(path string) []string
分隔環境變量裏面的路徑
func Walk(root string, walkFn WalkFunc) error
遍歷root目錄下的文件樹,並調用walkFn

package main

import (
   "fmt"
   "os"
   "path/filepath"
)

// 打印路徑名稱
func pathName(path string, info os.FileInfo, err error) error {
   if err != nil {
      return err
   } else {
      fmt.Println(path)
   }
   return nil
}

func main() {
   // 預定義變量
   fmt.Println(string(filepath.Separator), string(filepath.ListSeparator))

   // 返回path 相對當前路徑的絕對路徑
   dir := "/home/user/hello.go"
   real_dir, err := filepath.Abs(dir)
   fmt.Println(real_dir, err)

   // 返回path 的最短路徑
   dir = "/usr/../etc/../tmp"
   clear_dir := filepath.Clean(dir)
   fmt.Println(clear_dir) // /tmp

   // 返回targpath 相對 basepath路徑
   basepath, targpath := "/usr/local", "/usr/local/go/bin"
   rel_dir, err := filepath.Rel(basepath, targpath)
   fmt.Println(rel_dir, err) // go/bin <nil>

   // 返回軟鏈指向的路徑
   symlink := "/usr/local"
   real_dir, err = filepath.EvalSymlinks(symlink)
   fmt.Println(real_dir, err) // /usr/local <nil>

   // 返回路徑最前面的卷名
   root := "/usr/local/go"
   vol := filepath.VolumeName(root)
   fmt.Println(vol) // ‘‘

   // 路徑分隔符替換為 `/`
   slash_dir := filepath.ToSlash(root)
   fmt.Println(slash_dir) // /usr/local/go

   //  `/` 替換為路徑分隔符
   from_slash := filepath.FromSlash(slash_dir)
   fmt.Println(from_slash) // /usr/local/go

   // 分隔環境變量裏面的路徑
   env_path := "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/go/bin"
   env_slice := filepath.SplitList(env_path)
   for k, v := range env_slice {
      fmt.Println(k, v)
   }
   // 0 /usr/local/bin
   // 1 /usr/bin
   // 2 /bin
   // 3 /usr/sbin
   // 4 /sbin
   // 5 /opt/X11/bin
   // 6 /usr/local/go/bin

   // 遍歷 root 目錄下的文件樹,並調用 walkFn
   root_dir, err := os.Getwd()
   err = filepath.Walk(root_dir, pathName)
   fmt.Println(err)
}

4、目錄遍歷

Walk(root stirng, walkFn WalkFunc) error
// 參數root可以是文件名也可以是目錄名;walkFn是自定義的函數

func Walk(root string, walkFn WalkFunc) error {

   //獲取root的描述信息
   info, err := os.Lstat(root)
   if err != nil {
      //如果獲取描述信息發生錯誤,返回err由定義的walkFn函數處理
      err = walkFn(root, nil, err)
   } else {
      //調用walk(root, info, walkFn)函數進行遞歸遍歷root
      err = walk(root, info, walkFn)
   }
   if err == SkipDir {
      return nil
   }
   return err
}

Walk方法會遍歷root下的所有文件(包含root)並對每一個目錄和文件都調用walkFunc方法。在訪問文件和目錄時發生的錯誤都會通過error參數傳遞給WalkFunc方法。文件是按照詞法順序進行遍歷的,通常讓輸出更漂亮,但也會導致處理非常大的目錄時效率會降低。另外,Walk函數不會遍歷符號鏈接。
type WalkFunc func(path string, info os.FileInfo, err error)?
WalkFunc是一個方法類型,Walk函數在遍歷文件或者目錄時調用。調用時將參數傳遞給path,將Walk函數中的root作為前綴。將root + 文件名或者目錄名作為path傳遞給WalkFunc函數。例如在"dir"目錄下遍歷到"a"文件,則path="dir/a";Info是path所指向文件的文件信息。如果在遍歷過程中出現了問題,傳入參數err會描述這個問題。WalkFunc函數可以處理這個問題,Walk將不會再深入該目錄。如果函數會返回一個錯誤,Walk函數會終止執行;只有一個例外,我們也通常用這個來跳過某些目錄。當WalkFunc的返回值是filepaht.SkipDir時,Walk將會跳過這個目錄,照常執行下一個文件。

func walk(path string, info os.FileInfo, walkFn WalkFunc) error {

   //調用定義的walkFn自定義函數處理
   err := walkFn(path, info, nil)
   if err != nil {
      //返回錯誤,且該目錄可以跳過
      if info.IsDir() && err == SkipDir {
         return nil
      }
      return err
   }

   //如果是文件,則遍歷下一個
   if !info.IsDir() {
      return nil
   }

   //讀取該path下的所有目錄和文件
   names, err := readDirNames(path)
   if err != nil {
      //發生錯誤,調用自定義函數處理
      return walkFn(path, info, err)
   }

   //遍歷文件和目錄列表
   for _, name := range names {
      //路徑path/name
      filename := Join(path, name)
      //獲取該文件或者目錄信息
      fileInfo, err := lstat(filename)
      if err != nil {
         //發生錯誤,調用自定義函數處理
         if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir {
            return err
         }
      } else {
         //這裏遞歸調用,獲取root下各級文件和目錄信息,在自定義函數walkFn裏做處理
         err = walk(filename, fileInfo, walkFn)
         if err != nil {
            //遍歷文件發生錯誤或者目錄發生錯誤且不能跳過,則返回err
            if !fileInfo.IsDir() || err != SkipDir {
               return err
            }
         }
      }
   }
   return nil
}

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