• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

Go语言基础之15--文件基本操作

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

1.1 os.File

os.File封装所有文件相关操作, 是一个自定义的struct。

a. 打开一个文件进行读操作: os.Open(name string) (*File, error)

Open是以读的方式打开文件(底层调用的是Openfile函数)。

b. 关闭一个文件: File.Close()

1.2 文件操作简单实例

 示例1:

package main

import (
    "fmt"
    "io" //读取文件末尾特殊情况需要用到
    "os" //读取文件需要用到
)

func main() {
    filename := "c:/tmp.txt"
    file, err := os.Open(filename) //返回2个参数,第一个file指针,第二个错误值
    if err != nil {                //如果打开文件有错误,在这里输出
        fmt.Printf("open %s failed,err:%v\n", filename, err)
        return
    }
    defer func() { //打开一个文件,最后我们必须要将其关闭
        file.Close()
    }()

    var content []byte //定义1个变量存读取到的所有数据
    var buf [4096]byte //定义一个4k的字节数组,每次读取一点,4k读性能高
    for {
        n, err := file.Read(buf[:])      //将整个数组转换成切片读进去,Read函数返回2个参数,第1个n是读到的字节数,第二个是err
        if err != nil && err != io.EOF { //有一个特殊问题,当一个文件读读完,遇到文件末尾时,它也会返回一个错误,但是此时我已经读到文件末尾EOF,这个错误应该不算错误,所以应该把读到文件末尾这个错误给去掉。
            fmt.Printf("read %s failed, err:%v\n", filename, err)
            return //如果有错误就返回
        }

        if err == io.EOF {
            break //如果读取到文件末尾了,直接break退出。
        }

        vaildBuf := buf[0:n] //把有效数据都拿出来,不可能整个buf数组都是有效数据(最后一次读取到是很大可能是占据数组的一部分。),这里我们就需要借助切片。
        //fmt.Printf("%s\n", string(vaildBuf))
        content = append(content, vaildBuf...) //将有效的数据存到定义的变量切片中,另外将一个切片append到另一个切片中用...
    }
    fmt.Printf("content:%s\n", content)
}

 执行结果:

 

示例2:

通过函数传参

package main

import (
    "fmt"
    "io"
    "os"
)

func Read(filename string) (string, error) {
    //获得一个file
    f, err := os.Open(filename)
    if err != nil {
        fmt.Println("read fail")
        return "", err
    }

    //把file读取到缓冲区中
    defer f.Close()
    var content []byte
    var buf [1024]byte
    for {
        //从file读取到buf中, n表示本次读到的字节数
        n, err := f.Read(buf[:])
        if err != nil && err != io.EOF {
            fmt.Println("read buf fail", err)
            return "", err
        }
        //说明读取结束
        if err == io.EOF {
            break
        }
        //读取到最终的缓冲区中
        content = append(content, buf[:n]...)
    }
    fmt.Printf("content:%s\n", content)
    return string(content), nil
}

func main() {
    a := "c:/tmp.txt"
    Read(a)
}

 执行结果:

1.3 使用bufio提高文件读取性能

之前我们是直接从文件读,因为读取文件是一个相对比较慢的操作,所以当我们读一个大的文件时,或者又多次io操作时会影响性能,所以我们使用bufio(缓存机制,跟redis(缓存)有点类似,可以把文件操作类比成数据库操作)提高文件读取性能。

bufio包go语言已经帮我们封装好了。

bufio包总结如下:

1)bufio 包实现了带缓存的 I/O 操作

2)它封装一个 io.Reader 或 io.Writer 对象

3)使其具有缓存和一些文本读写功能

参考网址:https://blog.csdn.net/wangshubo1989/article/details/70177928

 

代码示例1:

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    filename := "c:/tmp.txt"
    file, err := os.Open(filename) //返回2个参数,第一个file指针,第二个错误值
    if err != nil {                //如果打开文件有错误,在这里输出
        fmt.Printf("open %s failed,err:%v\n", filename, err)
        return
    }
    defer func() { //打开一个文件,最后我们必须要将其关闭
        file.Close()
    }()

    reader := bufio.NewReader(file) //要封装这个文件读写,需要把文件传到bufio里面去,然后在bufio里面操作这个缓存,这里我们传的是file文件对象
    //为什么要传一个文件对象进去?   答:首先看缓存里有么有,有就读缓存,没有的话就用文件去读,也就是file这个文件对象(是一个结构体,里面包含很多用法)
    var content []byte //定义1个变量存读取到的所有数据
    var buf [4096]byte //定义一个4k的字节数组,每次读取一点,4k读性能高
    for {
        n, err := reader.Read(buf[:])    //将整个数组转换成切片读进去,Read函数返回2个参数,第1个n是读到的字节数,第二个是err
        if err != nil && err != io.EOF { //有一个特殊问题,当一个文件读读完,遇到文件末尾时,它也会返回一个错误,但是此时我已经读到文件末尾EOF,这个错误应该不算错误,所以应该把读到文件末尾这个错误给去掉。
            fmt.Printf("read %s failed, err:%v\n", filename, err)
            return //如果有错误就返回
        }

        if err == io.EOF {
            break //如果读取到文件末尾了,直接break退出。
        }

        vaildBuf := buf[0:n] //把有效数据都拿出来,不可能整个buf数组都是有效数据(最后一次读取到是很大可能是占据数组的一部分。),这里我们就需要借助切片。
        //fmt.Printf("%s\n", string(vaildBuf))
        content = append(content, vaildBuf...) //将有效的数据存到定义的变量切片中,另外将一个切片append到另一个切片中用...
    }
    fmt.Printf("content:%s\n", content)
}

 执行结果:

 

 

代码示例2:函数传参

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func Read(filename string) (string, error) {
    fi, err := os.Open(filename)
    if err != nil {
        return "", err
    }
    defer fi.Close()
    r := bufio.NewReader(fi)
    var content []byte
    var buf [1024]byte
    for {
        n, err := r.Read(buf[:])
        if err != nil && err != io.EOF {
            return "", err
        }
        if err == io.EOF {
            break
        }
        content = append(content, buf[:n]...)
    }
    fmt.Printf("content:%s\n", content)
    return string(content), nil
}

func main() {
    a := "c:/tmp.txt"
    Read(a)
}

 执行结果:

1.4 使用ioutil读取整个文件

针对小文件读取,十分方便

代码示例:

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    filename := "c:/tmp.txt"
    content, err := ioutil.ReadFile(filename)
    if err != nil {
        fmt.Printf("read file %s failed, err:%v\n", filename, err)
        return
    }
    fmt.Printf("content:%s\n", string(content))  //因为上面返回的是一个字节数组,所以必须转一下
}

执行结果:

 

 

代码示例2:使用函数传参

package main

import (
    "fmt"
    "io/ioutil"
)

func Read(filename string) (string, error) {
    content, err := ioutil.ReadFile(filename)
    if err != nil {
        return "", err
    }
    fmt.Printf("content:%s\n", content)
    return string(content), nil
}

func main() {
    a := "c:/tmp.txt"
    Read(a)
}

 执行结果:

1.5 读取压缩文件示例

gizp就相当于read,gzip使用传进去的文件对象去读取文件内容,解压完了在将数据返回。

下面我们通过几个例子来验证一系列理论

1.5.1 bufio读取压缩文件结果

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    fmt.Printf("start run...\n")

    filename := "c:/tmp.txt.gz"
    file, err := os.Open(filename)
    if err != nil {
        fmt.Printf("open %s failed, err:%v\n", filename, err)
        return
    }
    fmt.Printf("start0 read file\n")
    defer file.Close()

    reader := bufio.NewReader(file) 
    var content []byte 
    var buf [4096]byte 
    for {
        n, err := reader.Read(buf[:])   
        if err != nil && err != io.EOF { 
            fmt.Printf("read %s failed, err:%v\n", filename, err)
            return
        }

        if err == io.EOF {
            break
        }

        vaildBuf := buf[0:n] 
        content = append(content, vaildBuf...) 
    }
    fmt.Printf("content:%s\n", content)
}

 执行结果:

解释:

可以看到用bufio读取到的是压缩文件的二进制代码,也是乱码的。

1.5.2 读取压缩文件出现问题

这个问题目前只有读取压缩文件的实例存在,直接读取、bufio、ioutil都不存在这个问题,下面我们通过实例来演示一下。

package main

import (
    _ "bufio"
    "compress/gzip" //gzip要用到
    "fmt"
    "io"
    "os"
)

func main() {
    fmt.Printf("start run...\n")

    filename := "c:/tmp.txt.gz"
    file, err := os.Open(filename)
    if err != nil {
        fmt.Printf("open %s failed, err:%v\n", filename, err)
        return
    }
    fmt.Printf("start0 read file\n")
    defer file.Close()
    /*
        defer func() {
            file.Close()
        }()
    */

    reader, err := gzip.NewReader(file) //用gzip构建文件对象,reader就变成了一个读对象
    if err != nil {
        fmt.Printf("gzip read failed, err:%v\n", err)
        return
    }

    var content []byte
    var buf [100]byte
    for {
        //reader.Read
        fmt.Printf("start read file\n")
        n, err := reader.Read(buf[:])
        fmt.Printf("read %d  err:%v\n", n, err)
        if err != nil && err != io.EOF {
            fmt.Printf("read %s failed, err:%v\n", filename, err)
            return
        }

        //读到文件末尾了,文件已经读取完毕,Read方法会返回一个io.EOF错误。
        if err == io.EOF {
            break
        }
        validBuf := buf[0:n]
        content = append(content, validBuf...)
    }

    fmt.Printf("content:%s\n", content)
}

 执行结果:

解释:

可以发现出现了一个严重问题就是当我们每次读取100字节,分多次读取,会出现最后一次读取不到100字节(是52字节),但是err确是EOF,这个时候按照我们上面代码就会break退出,而此次读取到的52字节也会随之丢失,最终实际的结果也是造成了数据丢失。

1.5.3 读取压缩文件完整代码(解决文件丢失问题)

package main

import (
    _ "bufio"
    "compress/gzip" //gzip要用到
    "fmt"
    "io"
    "os"
)

func main() {
    fmt.Printf("start run...\n")

    filename := "c:/tmp.txt.gz"
    file, err := os.Open(filename)
    if err != nil {
        fmt.Printf("open %s failed, err:%v\n", filename, err)
        return
    }
    fmt.Printf("start0 read file\n")
    defer file.Close()
    /*
        defer func() {
            file.Close()
        }()
    */

    reader, err := gzip.NewReader(file) //用gzip构建文件对象,reader就变成了一个读对象
    if err != nil {
        fmt.Printf("gzip read failed, err:%v\n", err)
        return
    }

    //reader := bufio.NewReader(file)
    var content []byte
    var buf [100]byte
    for {
        //reader.Read
        fmt.Printf("start read file\n")
        n, err := reader.Read(buf[:])
        fmt.Printf("read %d  err:%v\n", n, err)
        if err != nil && err != io.EOF {
            fmt.Printf("read %s failed, err:%v\n", filename, err)
            return
        }

        if n > 0 { //做一个判断,只要有字节就追加到储存读取到内容的切片
            validBuf := buf[0:n]
            content = append(content, validBuf...)
        }

        //读到文件末尾了,文件已经读取完毕,Read方法会返回一个io.EOF错误。
        if err == io.EOF {
            break
        }
    }

    fmt.Printf("content:%s\n", content)
}

 执行结果:

因输出结果太长,此处截图只截取部分内容。

解释:

通过对读取的字节数进行判断,避免了数据丢失的问题。

1.5.4 补充

下面实例有部分用法可能会有疑问,但是写法比较标准,可以学习借鉴。

package main

import (
    "bufio"
    "compress/gzip"
    "fmt"
    "os"
)

func main() {
    fName := "c:/tmp.txt.gz"
    var r *bufio.Reader
    fi, err := os.Open(fName)
    if err != nil {
        fmt.Fprintf(os.Stderr, "%v, Can’t open %s: error: %s\n", os.Args[0], fName, err)
        os.Exit(1)
    }
    fz, err := gzip.NewReader(fi)
    if err != nil {
        fmt.Fprintf(os.Stderr, "open gzip failed, err: %v\n", err)
        return
    }
    r = bufio.NewReader(fz)
    for {
        line, err := r.ReadString('\n')
        if err != nil {
            fmt.Println("Done reading file")
            os.Exit(0)
        }
        fmt.Println(line)
    }
}

执行结果:

 

解释:

先用gizp解压,再用bufio缓存

1.6 文件写入

1)    os.OpenFile("filename",os.O_WRONLY|os.O_CREATE,066)

第一个参数是文件名

第二个参数是文件的打开模式:

os.O_WRONLY:只写

os.O_CREATE:创建文件

os.O_RDONLY:只读

os.O_RDWR:读写

os.O_TRUNC:清空

os.O_APPEND:文件存在以追加方式写入

 

第三个参数:权限控制

r-->4

w-->2

x-->1

2)    os.Create("output.dat")

用途:如果文件不存在需要先创建文件,用到的就是os.Create方法。(如果要创建的这个文件本来就存在,还要用Create(底层调用的Openfile函数)方法创建该文件,那么该文件就会被清空)

1.7 文件写入实例

 示例1:

package main

import (
    "fmt"
    "os"
)

func isFileExists(filename string) bool { //判断文件是否存在
    _, err := os.Stat(filename) //os.Stat会返回文件是否存在的相关属性
    if os.IsNotExist(err) {
        return false
    }
    return true
}

func main() {
    filename := "c:/tmp.txt"

    var file *os.File
    var err error
    if isFileExists(filename) {
        file, err = os.OpenFile(filename, os.O_APPEND, 0755) //如果文件存在则追加进去
        //file, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0755) //mac系统追加时还需要在加一个os.O_WRONLY方法
    } else {
        file, err = os.Create(filename) //不存在就创建该文件
    }

    if err != nil {
        fmt.Printf("open %s failed, err:%v\n", filename, err)
        return
    }

    defer file.Close()

    //给文件中写入内容
    n, err := file.WriteString("hello world") //WriteString可以传入字符串
    //io.WriteString(file,"hello world")   //io.WriteString也可以用来进行传入
    if err != nil {
        fmt.Printf("write failed, err:%v\n", err)
        return
    }
    fmt.Printf("write %d succ\n", n)
}

 执行结果:

查看文件内容:可以发现新追加的也添加进去了。

 

 

示例2:

package main

import (
    "io"
    "os"
)

func CheckFileExist(fileName string) bool {
    _, err := os.Stat(fileName)
    if os.IsNotExist(err) {
        return false
    }
    return true
}

func Write(fileName string) error {
    var f *os.File
    var err error
    if CheckFileExist(fileName) { //文件存在
        f, err = os.OpenFile(fileName, os.O_APPEND, 0666) //打开文件
        if err != nil {
            return err
        }
    } else { //文件不存在
        f, err = os.Create(fileName) //创建文件
        if err != nil {
            return err
        }
    }
    _, err = io.WriteString(f, "strTest")
    if err != nil {
        return err
    }
    return nil
}

func main() {
    Write("c:/tmp.txt")
}

 执行结果:

 

查看文件内容:

 

 

示例3:Fprintf

fmt包的Printf底层调用的就是Fprintf

实例如下:

package main

import (
    "fmt"
    "os"
)

func isFileExists(filename string) bool {
    _, err := os.Stat(filename)
    if os.IsNotExist(err) {
        return false
    }

    return true
}

func main() {
    filename := "c:/tmp.txt"

    var file *os.File
    var err error
    if isFileExists(filename) {
        //mac机器
        file, err = os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0755)
    } else {
        file, err = os.Create(filename)
    }

    if err != nil {
        fmt.Printf("open %s failed, err:%v\n", filename, err)
        return
    }

    defer file.Close()

    fmt.Fprintf(file, "%d %d is good", 100, 300) //这里主要是为了演示一下Printf的底层调用Fprintf,我们可以通过传入文件对象,将其写入文件
}

执行结果如下:

 

查看文件内容:

 

1.8 使用ioutil直接文件写入

ioutil相当于每次把要写入的内容以覆盖形式写入到文件内

实例1:

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    filename := "c:/tmp.txt"
    str := "dkfslfjdsklfjlskjflsjflsjflsjflks"
    err := ioutil.WriteFile(filename, []byte(str), 0755)
    if err != nil {
        fmt.Println("write fail")
    }
    fmt.Println("write success")
}

 执行结果:

查看文件内容:

 

实例2:

package main

import (
    "fmt"
    "io/ioutil"
)

func Write() {
    fileName := "c:/tmp.txt"
    strTest := "测试测试"
    var d = []byte(strTest)
    err := ioutil.WriteFile(fileName, d, 0666)
    if err != nil {
        fmt.Println("write fail")
    }
    fmt.Println("write success")
}

func main() {
    Write()
}

执行结果:

 

查看文件内容:

 

1.9 使用bufio进行文件写入 

bufio会减少写入文件的次数,先写到缓存中,然后会调用Flush方法将内存中的内容刷新到磁盘中。

bufio写入的缺点:

会造成文件丢失(当要写入的内容还在缓存中,还未被写入到磁盘时,程序挂了,那这些数据就丢失了。)

示例1:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func isFileExists(filename string) bool {
    _, err := os.Stat(filename)
    if os.IsNotExist(err) {
        return false
    }

    return true
}

func main() {
    filename := "c:/tmp.txt"

    var file *os.File
    var err error

    file, err = os.Create(filename)
    if err != nil {
        fmt.Printf("open %s failed, err:%v\n", filename, err)
        return
    }

    defer file.Close()

    writer := bufio.NewWriter(file)
    writer.WriteString("hello worldldfdsfsfsf")

    writer.Flush()
}

执行结果:

查看文件内容:

 

示例2:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func Write() {
    fileName := "c:/tmp.txt"
    f, err3 := os.Create(fileName) //创建文件
    if err3 != nil {
        fmt.Println("create file fail")
    }
    w := bufio.NewWriter(f)                //创建新的 Writer 对象
    n4, err3 := w.WriteString("bufferedn") //此时还是写入在内存中
    fmt.Printf("写入 %d 个字节\n 
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
go中的main函数和init函数发布时间:2022-07-10
下一篇:
go1.14.3安装micro报错处理记录发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap