基础-3

异常处理

  1. error接口:Go中的一个关于错误处理的标准模式,属于Go内建的接口类型;
    type error interface {
        Error() string
    }
    
  2. 使用方式:import "errors"
    func test(a int, b int) (value int, err error) {
        if b==0 {
            err = errors.New("0 不能作为除数!")
            return
        } else {
            value = a/b
            return
        }
    }
    func main() {
        value,err := test(1, 0)
        if err != nil {  // 发生了异常
            fmt.Println(err)  // 0 不能作为除数!
        } else {  // 运行通过
            fmt.Println(value)
        }
    }
    
  3. panic函数error返回的是一般性错误,而panic函数返回的是致命性错误;
    1. 比如数组角标越界、空指针引用等等,这些让程序崩溃的错误都会引起 panic 异常;
    2. 如果直接调用内置的 panic函数 也会引发panic异常,程序终止运行;
    3. panic函数接收任何值作为参数;
        func test() {
            panic("Hello Panic")
        }
        func main() {
            test()  //发生异常 panic: Hello Panic
        }
    
  4. defer:延迟调用,在当前函数执行结束之后,在执行 defer
        func test() {
            fmt.Println(111)
            defer fmt.Println(222)
            defer fmt.Println(333)
            fmt.Println(444)
        }
        func main() {
            fmt.Println(1)
            test()
            fmt.Println(2)
        }
        // 1 -> 111 -> 444 -> 333 -> 222 -> 2
    
    1. defer会被加载进栈区内存,而栈结构遵循先进后出,所以从后向前执行 defer 函数;
    2. 函数也可以声明为defer,但带有返回值的函数声明为defer时,不允许接收其返回值;
      func test() {
          fmt.Println(111)
          fmt.Println(222)
      }
      func main() {
          fmt.Println(1)
          defer test()
          fmt.Println(2)
      }
      // 1 -> 2 -> 111 -> 222
      
    3. defer函数内部如果使用了外部变量,那么在defer执行时,使用的是该变量最新的值;
      a,b := 1,2
      
      defer func(a int, b int) {
          fmt.Println(a)  // 1
          fmt.Println(b)  // 2
      }(a, b)  // 外部变量作为实参传递
      
      defer func() {  // 直接使用外部变量
          fmt.Println(a)  // 100
          fmt.Println(b)  // 200
      }()
      
      a = 100
      b = 200
      
  5. recover接口
    1. 运行时panic异常会引发程序崩溃,recover()则专门用于拦截panic异常,抢回控制权,避免程序崩溃;
    2. recover()只在 defer 函数中有效!
    3. 当有错误异常时,recover()返回错误信息,没有错误时,返回值为nil
        func test(i int) {
            var arr [5]int
            defer func() {
                err := recover()  // 拦截panic错误
                if err != nil {
                    fmt.Println(err)   // runtime error: index out of range
                }
            }()
            //角标越界,造成 panic 异常
            arr[i] = 1
            fmt.Println(arr)   //不再执行
        }
        func main() {
            test(10)
            fmt.Println("Hello healthy")   // Hello healthy
        }
    
    1. recover() 必须放在错误异常之前,否则是无效的!

文件操作

import "os"

文件创建

  1. 如果文件已存在,则清空文件,否则创建文件;
  2. os.Create("文件路径"):支持绝对路径也
    //打开/创建文件
    fp,err := os.Create("D:/a.txt")
    if err != nil {
        fmt.Println("文件创建失败!")
        return
    }
    //延迟调用,关闭文件
    defer fp.Close()
    

写入文件

  1. WriteString(string):写入字符串数据
    1. 第一个返回值是写入的字符串长度,第二个返回值是错误信息;
        fp,err := os.Create("D:/a.txt")
        if err != nil {
            fmt.Println("文件创建失败!")
            return
        }
        //延迟调用,关闭文件
        defer fp.Close()
        //写入数据
        n,err := fp.WriteString("Hello")
        fmt.Println(n)  // 5
        n,err = fp.WriteString("编程语言")
        fmt.Println(n)  // 12,Go语言中的一个汉字是3个字符,对应UTF-8就是3个字节
    
    1. 第一次写入都是从头开始,如果没有关闭,WriteString()每次都会追加写入。
  2. Write([]byte):写入字符/节切片,且字符串可以转为字符切片
        b := []byte{'h','e','l','l','o'}
        n,_ = fp.Write(b)  // n=5
    
        s := "hello"
        b = []byte(s)
        n,_ = fp.Write(b)  // n=5
    
  3. WriteAt([]byte, int64):指定偏移量,写入字符切片,光标默认在起始位置。

打开文件

Open()、OpenFile(),如果文件不存在,则打开失败;

  1. Open(name string):只读方式打开文件;
  2. OpenFile(name string, flag int, perm FileMode)
    1. flag:打开模式,常见有O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(可读可写)、O_APPEND(追加)
    2. perm:权限,取值范围0 - 7,一般使用 6
  3. Seek(offset int64, whence int):修改文件指针的位置;
    1. offset:偏移量,正数向右偏移,负数表示向左偏移;
    2. whence:指定光标的位置,io.SeekEnd 文件末尾,io.SeekStart 文件起始位置,io.SeekCurrent 文件的当前位置;
      fp,err := os.OpenFile("D:/a.txt", os.O_RDWR, 6)
      if err != nil {
          fmt.Println("文件打开失败!")
          return
      }
      defer fp.Close()
      b := []byte("World")
      n,_ := fp.Seek(0, io.SeekEnd)  // 文件末尾
      fp.WriteAt(b, n)   // 追加内容
      

读取文件

  1. Read([]byte):按照字节读取,读取的内容存入字符/节切片中;
    1. 返回值为文件内容的真实长度和错误信息;
    2. io.EOF:值为 -1,表示到了文件末尾;
    3. 返回的内容长度为 0 时,也可以认为到了文件末尾;
        b := make([]byte, 1024)  // 1k 大小的切片
        for {
            n,err2 := fp.Read(b)
            if err2 == io.EOF {  //到了文件结尾
                break
            }
            fmt.Print(string(b[:n]))  //每次循环读取的内容,把字符切片转为字符串
        }
    
  2. 按行读取:import bufio
    1. bufio.NewReader():创建带有缓冲区的reader
    2. ReadBytes() 在读取数据时,需要创建缓冲区,先将从文件中读取的数据存储于缓冲区中,然后将缓冲区中的数据取出来,写入硬盘中;
    3. 提供缓冲区的目的是,为了缓和CPU与磁盘设备之间的速度不匹配问题,文件缓冲区用于暂时存放读写期间的文件数据而在内存区域内预留的一定空间;
        fp,err := os.Open("D:/a.txt")  //以只读方式打开文件
        if err != nil {
            fmt.Println("文件打开失败!")
            return
        }
        defer fp.Close()
        //新建一个缓冲区
        reader := bufio.NewReader(fp)
        for {
            buf,err2 := reader.ReadBytes('\n')  //以换行符 \n 分割,每次读取一行内容
            
            fmt.Print(string(buf))  //每次循环读取的一行内容
    
            if err2 == io.EOF {  //到了文件结尾
                break
            }
        }
    
  3. 缓冲区分为系统缓冲和用户缓冲,因为磁盘的读写操作很慢,而缓冲区的速度接近光速,所以系统通常会先把从磁盘上读写的数据放在系统缓冲区中,等达到一定的量之后,才会进行下一步操作;
    1. ReadBytes() 其实是从系统缓冲区中读取的数据;
    2. NewReader()创建的是用户缓冲区。

目录操作

  1. OpenFile(name string, flag int, perm FileMode):对于目录来说,第三个参数perm通常传入os.ModeDir
  2. Readdir(n int):读目录,其中的内容称为目录项;
    1. n 表示读取目录项的成员个数,通常传-1,表示读取所有目录项;
    2. 返回值为FileInfo类型的切片,以及error信息,FileInfo内部保存了文件名;
        type FileInfo interface {
            Name() string
            Size() int64
            IsDir() bool
            Mode() FileMode
            ModTime() time.Time
            Sys() interface{}
        }
    
    1. 使用for range遍历FileInfo切片,通过 Name() 获取目录项名称,Size() 获取目录项大小,IsDir()判断目录还是文件。
  3. os模块中还有创建目录、删除文件、重命名、修改权限、管道等等函数。

字符串处理函数

import strings

  1. Contains(str, substr):是否包含某个字串,模糊查找;
  2. Join(a []string, sep string):拼接切片a 中的字符串,sep 表示连接符;
  3. Index(str, substr):查找一个子串在另一个字符串中出现的位置,查找失败则返回-1
  4. Repeat(str, count):重复字符串str count次,返回重复后的字符串;
    str := strings.Repeat("go", 3)
    fmt.Println(str)  // gogogo
    
    1. count 不能为负数;
    2. count=0 时,返回空字符串。
  5. Replace(str, oldstr, newstr, count):把str中的oldstr替换成newstrcount表示替换次数;
    1. count < 0表示全部替换。
  6. Split(str, sep):按照 sep 切割字符串,返回字符串切片;
  7. Trim(str, cut string):在str的头部和尾部去除所有的cut,比如去除前后的空格;
  8. Fields(str):去除空格符,并按照空格分隔字符串,返回有效数据的字符串切片;
  9. HasSuffix(str, suffix):判断字符串的后缀,是否以字串suffix结尾;
  10. HasPrefix(str, prefix):判断字符串的前缀,是否以字串prefix开头。

字符串转换

Go提供了字符串与其他类型之间相互转换的函数:import strconv

  1. Format系列函数:把其他类型的数据转为字符串;
    var str string
    // bool -> string
    str = strconv.FormatBool(false)
    // int -> string
    str = strconv.FormatInt(100, 10)  // 参数2表示基数/进制
    str = strconv.FormatInt(100, 8)  // 144
    str = strconv.FormatInt(100, 16)  // 64
    str = strconv.FormatInt(100, 2)  // 1100100
    
    str = strconv.Itoa(100)  // 也可以把整型转为字符串
    
    // f 表示以小数格式打印,5 表示小数点位数,64 表示以float64方式处理
    str = strconv.FormatFloat(3.1415926, 'f', 5, 64)
    
  2. Parse系列函数:把字符串数据转为其他类型;
    var flag bool
    var err error
    // string -> bool
    flag,err = strconv.ParseBool("false")
    if err==nil {  // 转换成功
        fmt.Println("转换成功: ", flag)
    } else {
        fmt.Println("转换失败: ", err)
    }
    
    // string -> int
    var flag int64
    var err error
    flag,err = strconv.ParseInt("12", 10, 64) // 十进制 int64
    flag,err = strconv.ParseInt("101011", 2, 64) // 43 按照二进制处理字符串,转为int64
    // 字符串转整型
    v, err = strconv.Atoi("12")
    
    // string -> float64
    flag,err = strconv.ParseFloat("3.1415926", 64)
    
  3. Append系列函数:将其他类型转为字符串后,添加到现有的字节切片中;
    //长度为0,容量为1024的字符切片
    slice := make([]byte, 0, 1024)
    slice = strconv.AppendBool(slice, true)
    //十进制方式转换
    slice = strconv.AppendInt(slice, 567, 10)
    slice = strconv.AppendFloat(slice, 3.14, 'f', 3, 64)
    slice = strconv.AppendQuote(slice, "hello")
    fmt.Println(string(slice)) // true5673.140"hello"
    

你可能感兴趣的:(基础-3)