分类 Golang 下的文章

Golang-缓冲通道&定向通道

1.非缓冲通道:发送和接收到一个未缓冲的通道是阻塞的;一次发送操作对应一次接收操作;对于一个goroutine来讲,它的一次发送,在另一个goroutine接受之前都是阻塞的,同样的,对于接收来讲,在另一个goroutine发送之前,它也是阻塞的

2.缓冲通道:指一个通道,带有一个缓冲区。发送到一个缓冲通道只有在缓冲区满时才被阻塞。类似地,从缓冲通道接收的信息只有在缓冲区为空时才会被阻塞

  • 语法:ch := make(chan type, capactity);capactity应该大于零,以便通道具有缓冲区;无缓冲通道的容量为0,因此之前创建通道时省略了容量参数
func main() {
    ch3 := make(chan string, 4)
    go sendData(ch3)

    for{
        v, ok := <-ch3
        if !ok{
            fmt.Println("读完了。。。", ok)
            break
        }
        fmt.Println("读取的数据是:", v)

    }

    fmt.Println("main...over...")
}

func sendData(ch chan string)  {
    for i:= 0; i<10; i++ {
        ch <- "数据" + strconv.Itoa(i)
        fmt.Printf("子goroutine中发送的第 %d 个数据\n", i)
    }
    close(ch)
}

3.定向通道

  • 双向通道:一个goroutine可以向通道发送数据,也可以接收数据
双向通道:
        chan
            chan <- data,发送数据,写出
            data <- chan,获取数据,读取
单向通道:定向通道
        chan <- T, 只支持写
        <- chan T, 只支持读
ch1 := make(chan string)

    done := make(chan bool)
    go sendData(ch1, done)

    data := <- ch1 //读取
    fmt.Println("子goroutine传来的数据是:", data)

    ch1 <- "我是main的数据" //发送数据

    <- done//因为阻塞,所以会等待子goroutine的 通道done发送数据
    fmt.Println("main...over...")

    //time.Sleep(1)

}

func sendData(ch1 chan string, done chan bool)  {
    ch1 <- "我是lzc" //发送

    data := <- ch1 //读取数据
    fmt.Println("main goroutine传来的数据:", data)

    done <- true
}
  • 定向通道也叫单向通道
单向:单向
            chan <- T, 只支持写
            <- chan T, 只读
ch2 := make(chan <- int)//单向,只能写,不能读
ch2 <- 100
data := <- ch2
invalid operation: <-ch2 (receive from send-only type chan<- int)


ch3 := make(<- chan int)//单向,只能读,不能写
ch3 <- 100
invalid operation: ch3 <- 100 (send to receive-only type <-chan int)

Golang-channel通道初识

1.通道可以被认为是Goroutines通信的管道;当多个goroutines想共享数据的时候,虽然也提供了传统的同步机制(同步等待组、同步锁),但是Go语言强烈建议的是使用Channel通道来实现goroutine之间的通信;

2.通道的声明和定义一个变量的语法一样; 通道是引用型数据

//声明通道
var 通道名 chan 数据类型
//创建通道:如果通道为nil(就是不存在),就需要先创建通道
通道名 = make(chan 数据类型)

func main(){
    var a chan int
    if a == nil {
        fmt.Println("channel 是nil的,不能使用,需要先创建通道。")
    }
    a = make(chan int)
    fmt.Printf("数据类型:%T", a)

}

//也可以简短声明:
a := make(chan int)

3.通道的注意点

  • 用于goroutine,传递消息
  • 每个通道都有相关联的数据类型
  • 使用通道传递数据 <-
  • chan <-data,发送数据到通道。向通道中写数据
  • data <-chan,从通道中获取数据。从通道中读取数据
  • 阻塞:发送数据 chan <- data,阻塞的,直到另一条goroutine,读取数据来解除阻塞;读取数据,data <- chan,也是阻塞的。直到另一条goroutine,写出数据解除阻塞;
  • 本身channel就是同步的,意味着同一时间,只能有一条goroutine来操作
func main() {
    var ch1 chan bool
    ch1 = make(chan bool)

    go func() {
        for i:=0; i<10; i++ {
            fmt.Println("子goroutine中,i:", i)
        }
        //循环结束,向通道中写数据,表示要结束了。。。
        ch1 <- true
        fmt.Println("结束")
    }()


    //如果主进程先抢占资源,会去chan中读数据,如果没有数据,会阻塞下去..
    data := <- ch1
    fmt.Println("main...data-->:", data)
    fmt.Println("main主进程结束...")
}
//阻塞式通道
func main() {
    ch1 := make(chan int)

    go func() {
        fmt.Println("子goroutine开始执行")

        //time.Sleep(2*time.Second)
        data := <- ch1 //从ch1中读取数据
        fmt.Println("data:", data)
    }()

    //time.Sleep(5*time.Second)
    ch1 <- 10
    fmt.Println("main...over...")
}
//死锁:使用通道的时候要考虑的一个重要因素;只有发送数据,没有接受数据;或者只有接受数据,没有发送数据

func main()  {
    ch1 := make(chan int)
    ch1 <- 5
}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()

3.关闭通道和通道上范围循环

· 关闭通道,发送者可以关闭通道,来通知接受方不会有更多的数据发送到channel上: close(ch);接受者可以在接收来自通道的数据时使用额外的变量来检查通道是否已经关闭:v,ok := <-ch, 如果ok的值是true,表示成功的读取了一个数据value。如果为false,意味着正在从一个封闭的通道读取数据,从封闭通道读取的值为零值(根据ch的类型的零值)

关闭通道:close(ch)
    子goroutine:写出10个数据
        每写一个,阻塞一次,主goroutine读取一次,解除阻塞

    主goroutine,读取数据
        每次读取数据,阻塞一次,子goroutine,写出一个,解除阻塞

func main() {
    
    ch1 := make(chan int)
    go sendData(ch1)

    for{
        time.Sleep(1*time.Second)
        v, ok := <- ch1
        if !ok {
            fmt.Println("已经读取了所有的数据。。。", ok)
            break
        }
        fmt.Println("读取的数据:", v, ok)
    }
    fmt.Println("main..over...")

}

func sendData(ch1 chan int)  {
    //发送方:10条数据
    for i:=0; i <10; i++ {
        ch1 <- i
    }
    close(ch1)
}

读取的数据: 0 true
读取的数据: 1 true
读取的数据: 2 true
读取的数据: 3 true
读取的数据: 4 true
读取的数据: 5 true
读取的数据: 6 true
读取的数据: 7 true
读取的数据: 8 true
读取的数据: 9 true
已经读取了所有的数据。。。 false
main..over...
  • 通道上的范围循环
func main() {

    ch1 := make(chan int)
    go sendData(ch1)

    //for循环的 for range形式可用于从通道接收值,直到它关闭为止,不需要单独判断
    for v := range ch1{//从通道读取数据,如果没值,则阻塞
        fmt.Println("读取数据:", v)
    }
    fmt.Println("main..over.....")

}

func sendData(ch1 chan int)  {
    //发送方:10条数据
    for i:=0; i <10; i++ {
        time.Sleep(1*time.Second)
        ch1 <- i
    }
    close(ch1)//通知对方,通道关闭;否则会发生死锁
}

Golang-临界资源

001#1. 临界资源:指并发环境中多个进程、线程、协程共享的资源;但是在并发编程中对临界资源的处理不当,往往会导致数据不一致的问题

func main() {
    /*
    临界资源
    */
    a := 1
    go func() {
        a = 2
        fmt.Println("goroutine...", a)
    }()


    a = 3
        
    fmt.Println("main goroutine", a)
    
}
因为主的协程执行完之后,关闭了资源,所以子协程没有来的及执行:
main goroutine 3
func main() {
    /*
    临界资源
    */
    a := 1
    go func() {
        a = 2//14行
        fmt.Println("goroutine...", a)
    }()

    a = 3//18行
    time.Sleep(1)
    fmt.Println("main goroutine", a)
    
}
主协程先把赋值为3,在要打印还没打印的时候,睡去之后,子协程持有资源又将其赋值为2
goroutine... 2
main goroutine 2
通过命令行来看一下:
$ go run -race demo10_race.go
==================
//警告:有临界资源
WARNING: DATA RACE
//通过goroutine 6在0x00c00000a0e0处写入:
Write at 0x00c00000a0e0 by goroutine 6:
  main.main.func1()
      E:/GoPath/src/Go_Advanced/demo10_race.go:14 +0x43

//通过主goroutine在0x00c00000a0e0上一次写入:
Previous write at 0x00c00000a0e0 by main goroutine:
  main.main()
      E:/GoPath/src/Go_Advanced/demo10_race.go:18 +0x8f

Goroutine 6 (running) created at:
  main.main()
      E:/GoPath/src/Go_Advanced/demo10_race.go:13 +0x81
==================
main goroutine 2
goroutine... 2
Found 1 data race(s)
exit status 66

2. 并发本身并不复杂,但是因为有了资源竞争的问题,就会有许多莫名其妙的事情

var ticket = 10
func main() {

    go sale("售票口1")
    go sale("售票口2")
    go sale("售票口3")
    go sale("售票口4")

    time.Sleep(5*time.Second)
}

func sale(name string){
    rand.Seed(time.Now().UnixNano())
    for  {
        if ticket > 0  {
            time.Sleep(time.Duration(rand.Intn(1000))*time.Microsecond)
            fmt.Println(name,"售出:", ticket)
            ticket--
        } else {
            fmt.Println("售完票了")
            break
        }
    }

}

售票口4 售出: 10
售票口2 售出: 10
售票口1 售出: 10
售票口3 售出: 10
售票口4 售出: 6
售票口1 售出: 6
售票口2 售出: 5
售票口3 售出: 5
售票口1 售出: 2
售票口2 售出: 1
售完票了
售票口4 售出: 1
售完票了
售票口3 售出: 0
售完票了
售票口1 售出: -2
售完票了

3. 解决:很多其他语言都是同步方式,通过上锁的方式,及某一段时间段,只能允许一个协程来访问这个共享数据, sync同步包;

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func main() {

    /**
    WaitGroup: 同步等待组
        Add(), 设置等待组中要执行的子 goroutine的数量

        Wait(), 让主goroutine处于等待


        Done(),让等待组中的计数器减一,=在子goroutine中结束等待
     */

    wg.Add(2)

    go fun1()
    go fun2()

    fmt.Println("main 进入阻塞状态,等待wg子goroutine结束")

    wg.Wait() //表示goroutine进入等待,意味着阻塞

    fmt.Println("main..结束阻塞")
}

func fun1()  {
    for i:= 1; i<10; i++ {
        fmt.Println("fun1()函数中打印...A", i)
    }

    wg.Done()//执行完会后,将计数器减1 wg.Add(-1)
}
func fun2()  {

    defer wg.Done()
    for j:= 1; j<10; j++ {
        fmt.Println("\tfun2()函数中打印...A", j)
    }
}

4. go并不鼓励用锁来保护临界资源,而是鼓励通过channel将共享状态的变化在各个协程之间传递;要以通信的方式去共享内存;

  • 互斥锁(同步):最简单的,也是最暴力的;降低了性能
var ticket = 10

var mutex sync.Mutex

var wg sync.WaitGroup
func main() {


    wg.Add(4)
    go sale("售票口1")
    go sale("售票口2")
    go sale("售票口3")
    go sale("售票口4")

    wg.Wait()//main要等待
    fmt.Println("程序结束了")
    //time.Sleep(5*time.Second)
}

func sale(name string){
    rand.Seed(time.Now().UnixNano())
    defer wg.Done()
    for  {

        mutex.Lock()
        if ticket > 0  {
            time.Sleep(time.Duration(rand.Intn(1000))*time.Microsecond)
            fmt.Println(name,"售出:", ticket)
            ticket--
        } else {
            mutex.Unlock()
            fmt.Println("售完票了")
            break
        }

        mutex.Unlock()
    }

}
  • 读写锁:针对读或写的互斥锁,锁可以由任意数量的读取器或单个编写器持有;基本遵循两大原则:可以随便读,多个goroutine同时读;写的时候啥也不能干,不能读也不能写。
var rwMutex *sync.RWMutex
var wg *sync.WaitGroup
func main() {
    rwMutex = new(sync.RWMutex)
    wg = new(sync.WaitGroup)

    wg.Add(2)

    go readData(1)
    go readData(2)

    wg.Wait()
    fmt.Println("main over...")

}

func readData(i int){
    defer wg.Done()
    fmt.Println(i, "开始读: read start...")

    rwMutex.RLock()//读操作上锁
    fmt.Println(i, "正在读取数据:reading...")
    time.Sleep(1*time.Second)
    rwMutex.RUnlock()//读操作解锁
    fmt.Println(i, "读结束:read over...")

}
2 开始读: read start...
2 正在读取数据:reading...
1 开始读: read start...
1 正在读取数据:reading...
2 读结束:read over...
1 读结束:read over...
main over...

读操作可以同时进行
var rwMutex *sync.RWMutex
var wg *sync.WaitGroup
func main() {
    rwMutex = new(sync.RWMutex)
    wg = new(sync.WaitGroup)

    wg.Add(3)

    go writeData(1)
    go readData(2)
    go writeData(3)

    wg.Wait()
    fmt.Println("main over...")

}

func writeData(i int)  {
    defer wg.Done()
    fmt.Println(i, "开始写: write start...")

    rwMutex.Lock()//读操作上锁
    fmt.Println(i, "正在写数据:writing...")
    time.Sleep(3*time.Second)
    rwMutex.Unlock()//读操作解锁
    fmt.Println(i, "写结束:write over...")

}
3 开始写: write start...
3 正在写数据:writing...
2 开始读: read start...
1 开始写: write start...
明显卡顿:必须等3写完,解锁之后才可以;这个时候其他两个协程(1,2)是不能操作的
3 写结束:write over...
2 正在读取数据:reading...
2 读结束:read over...
1 正在写数据:writing...
1 写结束:write over...
main over...

Golang进阶-runtime包

1. 包含了go运行时系统交互的操作,比如控制goroutines的函数,还包括了反射包的使用;类似java和.net的虚拟机,管理包括内存分配,垃圾回收等

func main() {
    //获取goroot的目录
    fmt.Println("GOROOT:", runtime.GOROOT())
    //获取操作系统
    fmt.Println("os:",runtime.GOOS)
    //获取cpu的数量
    fmt.Println("逻辑cpu的数量:",runtime.NumCPU())

    //设置go程序执行的最大cpu的数量:[1,256]
    n := runtime.GOMAXPROCS(runtime.NumCPU());
    fmt.Println(n)

    //gosched, 让出cpu
    go func() {
        for i := 0; i < 5; i++ {
            fmt.Println("go1...")
        }
    }()

    for i:= 0; i<4; i++ {
        //让出时间片,先让别的goroutine执行
        runtime.Gosched()
        fmt.Println("main...")
    }

    runtime.Goexit()
    //终止当前的goroutine
}

Golang进阶-goroutine

1. go中使用goroutine来实现concurrently

  • goroutines非常便捷便宜,只有堆栈大小的几个kb,可以根据应用程序的需要增长和收缩;而在线程的情况下,堆栈大小必须指定并且是固定的

2. 主goroutine:封装main函数的goroutine称为主goroutine;

  • 主goroutine结束了,子goroutine也会都结束;不管运行完没有

3. 在函数或方法调用前面加上关键字go;没有返回值

package main

import (
    "fmt"
    "time"
)

func main() {
    go hello()
    fmt.Println("mafin function")
}

func hello()  {
    fmt.Println("hello world goroutine")
}

输出:main function

Golang进阶-断点续传

1. 主要借助io包里的Seeker接口

type Seeker interface {
    Seek(offset int64, whence int) (int64, error)

    //设置指针光标的位置:
        第一个参数:偏移量,
        第二个参数:如何设置:
            const (
            SeekStart   = 0 // 相对于光标开头
            SeekCurrent = 1 // 相对于光标当前
            SeekEnd     = 2 // 相对于光标结尾
            )
}

fileName := "E:/GoPath/src/a/aa.txt"
    //打开文件,注意是否有此文件

    //file, err := os.Open(fileName)
    file, err := os.OpenFile(fileName, os.O_RDWR, os.ModePerm)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    bs := []byte{0}
    file.Read(bs)
    fmt.Println(string(bs))

    file.Seek(4, io.SeekStart)
    file.Read(bs)
    fmt.Println(string(bs))

    file.Seek(4, io.SeekCurrent)
    file.Read(bs)
    fmt.Println(string(bs))

    file.Seek(0, io.SeekEnd)
    file.WriteString("ABC")

2. 断点续传应用场景(文件的传递:文件的复制)

  • 文件比较大,如何缩短耗时?
  • 如果文件传递过程中,程序被迫中断,再次重启是否需要重头开始?
  • 传递过程中,是否支持暂停和恢复?

3. 原理:通过一个临时文件记录已传递文件的大小,字节的位置; 思路:边复制,边记录复制的总量

func main() {

    srcFile := "E:/GoPath/src/a/1.png"
    //获取文件名
    destFile := srcFile[strings.LastIndex(srcFile, "/")+1: ]
    fmt.Println(destFile)
    temFile := destFile + "temp.txt"
    fmt.Println(temFile)

    file1,err := os.Open(srcFile)
    HandleErr(err)
    file2,err := os.OpenFile(destFile, os.O_CREATE|os.O_WRONLY, os.ModePerm)
    HandleErr(err)
    file3,err := os.OpenFile(temFile, os.O_CREATE|os.O_RDWR, os.ModePerm)
    HandleErr(err)

    defer file1.Close()
    defer file2.Close()

    //1: 先读取临时文件的数据,再seek
    file3.Seek(0, io.SeekStart)//从0开始读
    bs := make([]byte, 100,100)//一个文件的长度
    n1,err := file3.Read(bs)
    //HandleErr(err)
    countStr := string(bs[:n1])
    count,err := strconv.ParseInt(countStr, 10, 64)
    //HandleErr(err)
    fmt.Println(count)

    //2:设置偏移量;设置读,写的位置
    file1.Seek(count, io.SeekStart)
    file2.Seek(count, io.SeekStart)
    data := make([]byte, 1024, 1024)//用来复制文件
    n2 := -1 //读取的数据量
    n3 := -1 //写出的数据量
    total := int(count) //读取的总量

    //3: 复制文件
    for{
        n2,err = file1.Read(data)
        if err == io.EOF || n2 == 0 {
            fmt.Println("复制完毕", total)
            file3.Close()
            os.Remove(temFile)
            break
        }

        n3,err = file2.Write(data[:n2])
        total += n3

        //将复制的总量,存储到临时文件中
        file3.Seek(0, io.SeekStart)
        file3.WriteString(strconv.Itoa(total))

        fmt.Print("total:%d\n", total)


        //假装断电
        if total > 8000{
            //panic("假装断电看看")
        }
    }




}

func HandleErr(err error){
    if err != nil {
        log.Fatal(err)
    }
}

Golang进阶-复制文件

1. 文件复制原理:读取源文件的数据,把读到的数据再写到目标文件中;如果文件比较小,一次读写就可以了;如果文件比较大,则便边读边写

2. 使用io.read和io.write来实现

func main() {
    srcFile := "E:/GoPath/src/a/1.png"
    desFile := "E:/GoPath/src/a/2.png"
    total,err := CopyFile1(srcFile, desFile)
    fmt.Println(total, err)
}

func CopyFile1(srcFile, destFile string)(int, error)  {
    filel,err := os.Open(srcFile)
    if err != nil {
        return 0, err
    }

    file2,err := os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm)
    if err != nil {
        return 0, err
    }

    defer filel.Close()
    defer file2.Close()

    //读写
    bs := make([]byte, 1024, 1024)
    n := -1 //读取的数据量
    total := 0
    for{
        n,err = filel.Read(bs)
        if err == io.EOF || n == 0 {
            fmt.Println("拷贝完毕")
            break
        } else if err != nil {
            fmt.Println("报错了:", err)
            return total, err
        }
        total += n
        file2.Write(bs[:n])
    }

    return total,nil
}

如果文件比较大,可以把切片bs的长度写大一些

3. 使用io包的copy方法

func CopyFile2(srcFile, destFile string)(int64, error)  {
    file1,err := os.Open(srcFile)
    if err != nil {
        return 0, err
    }

    file2,err := os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm)
    if err != nil {
        return 0, err
    }

    defer file1.Close()
    defer file2.Close()

    return io.Copy(file2, file1)
}

4. 使用ioutil的读写,一次性读取,一次性写入;不适合较大的文件复制,容易内存溢出

func CopyFile3(srcFile, destFile string)(int, error)  {
    bs,err := ioutil.ReadFile(srcFile)
    if err != nil {
        return 0,err
    }
    err = ioutil.WriteFile(destFile,bs,0777)
    if err != nil {
        return 0,err
    }
    return len(bs), nil
}

5. 总结,后两种方法的性能还是不错的;扩展:CopyN(), CopyBuffer();无论哪个copy方法最终都是有copyBuffer这个私有方法实现的

Golang进阶-io操作

1. I/O操作也叫输入输出操作。I指Input,O指Output;用于读写数据的,有些语言也叫流操作,是指数据通信的通道;

2. io包里最重要的是两个接口:Reader和Writer接口

3. Reader接口

type Reader interface {
    Read(p []byte) (n int, err error)
}
它返回读取的字节数(0 <= n <= len(p))和遇到的任何错误

4. 读取本地aa.txt文件的数据 (asdfgryuio)

  • 打开文件
fileName := "E:/GoPath/src/a/aa.txt"
    file, err := os.Open(fileName)
    if err != nil {
        fmt.Println("err: ", err)
        return
    }
  • 读取数据
//读取数据
    bs := make([]byte, 4, 4)
    //第一次读取
    n, err := file.Read(bs)
    fmt.Println(err)//<nil>
    fmt.Println(n)//4
    fmt.Println(bs)//[97 115 100 102]返回的是每个元素的字节编码数值
    fmt.Println(string(bs))//asdf

    //第二次读取
    n, err = file.Read(bs)
    fmt.Println(err)//<nil>
    fmt.Println(n)//4
    fmt.Println(bs)//[103 114 121 117]返回的是每个元素的字节编码数值
    fmt.Println(string(bs))//gryu

    //第三次读取
    n, err = file.Read(bs)
    fmt.Println(err)//<nil>
    fmt.Println(n)//2
    fmt.Println(bs)//[105 111 121 117]返回的是每个元素的字节编码数值
    fmt.Println(string(bs))//ioyu


    //第四次读取
    n, err = file.Read(bs)
    fmt.Println(err)//EOF
    fmt.Println(n)//0

//读取数据
    bs := make([]byte, 4, 4)
    n := -1
    for  {
        n, err = file.Read(bs)
        if  n == 0 || err == io.EOF{
            fmt.Println("到了文件末尾,结束读取...")
            break
        }
        fmt.Println(string(bs[:n]))

    }
  • 关闭文件
//可以用defer先关闭掉,以防漏写,造成资源泄露
    defer file.Close()

5. Writer接口

type Writer interface {
    Write(p []byte) (n int, err error)
}

6. 写入到本地文件ab.txt

  • 打开文件(已存在的文件用open,不存在的用openFile)
//file, err := os.Open(fileName)
    file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)


    if err != nil {
        fmt.Println("err: ", err)
        return
    }
  • 关闭文件 (以防忘掉,造成资源泄露)
defer file.Close()
  • 写入内容
//写入数据
    //bs := []byte{65,66,67,68,69,70}
    bs := []byte{97,98,99,100}
    //n,err := file.Write(bs)
    n,err := file.Write(bs[:2])
    fmt.Println(n)
    HandleErr(err)
    file.WriteString("\n")

    //直接写出字符串
    n,err = file.WriteString("Hello world")
    fmt.Println(n)
    HandleErr(err)
    file.WriteString("\n")

    //切片也可以直接写入字符串
    n,err = file.Write([]byte("totay"))
    fmt.Println(n)
    HandleErr(err)

func HandleErr(err error){
    if err != nil {
        log.Fatal(err)
    }
}

7. writeAt方法指定从什么位置开始写,对应readAt从什么位置开始读

Golang进阶-file文件操作

1.file类是在os包中,封装了底层的文件描述符和相关信息,同时封装了Read和Write的实现;

2.文件信息通过一个接口来实现:FileInfo;如何操作这个接口的方法呢:os.Stat获取fileinfo的返回值

type FileInfo interface {
    Name() string       // base name of the file 文件名. 扩展名
    Size() int64        // 文件大小
    Mode() FileMode     // 文件权限
    ModTime() time.Time // 修改时间
    IsDir() bool        // 是否是文件
    Sys() interface{}   // 基础数据源接口(can return nil)
}

fileinfo, err := os.Stat("E:/GoPath/src/a/aa.txt")
if err != nil {
    fmt.Println("err: ", err)
}
fmt.Printf("%T\n",fileinfo)

//文件名
fmt.Println(fileinfo.Name())
//大小
fmt.Println(fileinfo.Size())
//是否是目录
fmt.Println(fileinfo.IsDir())
//修改时间
fmt.Println(fileinfo.ModTime())
//权限
fmt.Println(fileinfo.Mode())

3文件操作

  • 路径操作
fileName1 := "E:/GoPath/src/a/aa.txt"
//路径:是否是绝对路径
fmt.Println(filepath.IsAbs(fileName1))

//绝对路径
fmt.Println(filepath.Abs(fileName1))

//上级路径
fmt.Println(path.Join(fileName1,".."))
  • 创建目录
//Mkdir只能创建一层目录
err := os.Mkdir("E:/GoPath/src/b", os.ModePerm)
if err != nil {
    fmt.Println("err: ", err)
    return
}
fmt.Println("创建b目录成功")

//MkdirAll能创建多层
err := os.MkdirAll("E:/GoPath/src/a/cc/dd/ee", os.ModePerm)
if err != nil {
    fmt.Println("err: ", err)
    return
}
fmt.Println("多层目录创建成功")
  • 创建文件
//创建文件: create采用模式06666(任何人可读写,不可执行),已存在的文件会截断(内容清空),不存在则新建
file1, err := os.Create("E:/GoPath/src/a/ab.txt")
if err != nil {
    fmt.Println("err: ", err)
}
fmt.Println(file1)
  • 打开文件
//文件权限
const (
    // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
    O_RDONLY int = syscall.O_RDONLY // open the file read-only.
    O_WRONLY int = syscall.O_WRONLY // open the file write-only.
    O_RDWR   int = syscall.O_RDWR   // open the file read-write.
    // The remaining values may be or'ed in to control behavior.
    O_APPEND int = syscall.O_APPEND // append data to the file when writing.
    O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
    O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
    O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
    O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)

//只读
file3, err := os.Open("E:/GoPath/src/a/ab.txt")
if err != nil {
    fmt.Println("err: ", err)
}
fmt.Println(file3)

//指定权限: (文件名称,文件的打开方式,文件的的权限:打开的文件如果不存在,so需要指定权限)
file4, err := os.OpenFile("E:/GoPath/src/a/ab.txt", os.O_RDONLY|os.O_WRONLY, os.ModePerm)
if err != nil {
    fmt.Println("err: ", err)
}
fmt.Println(file4)
  • 关闭文件:文件名称.Close()
  • 删除文件或文件夹
err := os.Remove("E:/GoPath/src/a/aa.txt")
    if err != nil {
        fmt.Println("err: ", err)
        return
    }

fmt.Println("删除文件成功")

//强制删除,所有
err := os.RemoveAll("E:/GoPath/src/a/cc/dd")
    if err != nil {
        fmt.Println("err: ", err)
        return
    }

fmt.Println("删除多层文件夹成功")

Golang进阶-time包

1:time相关转化

1秒=1000毫秒,millisecond
1毫秒=1000微妙,microsecond-->us
1微秒=1000纳秒,nanosecond-->ns
1纳秒=1000皮秒,picosecond-->ps

2:常用函数

//1获取现在的时间
    t1 := time.Now()
    fmt.Printf("%T\n", t1)
    fmt.Println(t1)

    //2获取指定的时间
    t2 := time.Date(2008,7,16,16,30,28,0,time.Local)
    fmt.Println(t2)

    //3格式化时间 time-->string之间的转换(里面的模板时间必须为 2006.1.2 15:04:05)
    s1 := t1.Format("2006年1月2日 15:04:05")
    fmt.Println(s1)
    fmt.Printf("%T\n", s1)

    s2 := t1.Format("2006-1-2")
    fmt.Println(s2)
    fmt.Printf("%T\n", s2)

    //string-->time日期类型(模板时间同上,且格式需相同于数据源)
    s3 := "1999年10月10日"//sting
    t3 ,err:= time.Parse("2006年1月2日", s3)
    if err != nil {
        fmt.Println("err", err)
    }
    fmt.Println(t3)
    fmt.Printf("%T\n", t3)

    fmt.Println(t1.String())

    //4.根据当前时间,获取指定的内容
    year,month,day := t1.Date()
    fmt.Println(year,month,day)

    hour,min,sec := t1.Clock()
    fmt.Println(hour,min,sec)


    //5.时间戳:指定的的日期,距离1970年1月1日0点0时0分0秒的时间差值
    t4 := time.Date(1970,1,1,1,0,0,0,time.UTC)
    timeStamp1 := t4.Unix()
    fmt.Println(timeStamp1)

    timeStamp2 := t1.Unix()
    fmt.Println(timeStamp2)

    timeStamp3 := t4.UnixNano()
    fmt.Println(timeStamp3)

    timeStamp4 := t1.UnixNano()
    fmt.Println(timeStamp4)


    //6.时间间隔
    t5 := t1.Add(time.Minute)
    fmt.Println(t1)
    fmt.Println(t5)
    fmt.Println(t1.Add(24*time.Hour))

    //差值
    t6 := t5.Sub(t1)
    fmt.Println(t6)

    //7.睡眠
    time.Sleep(3 * time.Second)
    fmt.Println("main...over...")