• Go文件操作


    对于文件,我们并不陌生,文件是数据源(保存数据的地方)的一种,比如大家经常使用的word文档,txt文件,Excel文件...等等都是文件。文件最主要的作用就是保存数据,它既可以保存一张图片,也可以保存视频,声音......
    文件在程序中是以流的形式来操作的。

    :数据在数据源(文件)和程序(内存)之间经历的路径
    输出流:数据从程序(内存)到数据源(文件)的路径
    输入流:数据从数据源(文件)到程序(内存)的路径
    输入与输出都是相对于内存而言的,从内存向外流就是输出,从外部向内存流就是输入

    在Go中,我们操作文件的方法在os包中,会经常使用到os.File结构体 Go语言标准库文档

    示例1: 打开和关闭文件

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
    
        //打开文件(/Users/xxx/Go/src/file.txt)
        //概念说明:file的叫法
        //1.file 叫 file对象
        //2.file 叫 file指针
        //3.file 叫 file文件句柄
        file, err := os.Open("/Users/itbsl/Go/src/file.txt")
        if err != nil {
            fmt.Println("文件打开失败,原因是:", err)
            //os.Exit(0)
        }
        defer func() {
            //文件及时关闭
            err = file.Close()
            if err != nil {
                fmt.Println("文件关闭失败,原因是", err)
            }
        }()
    }
    

    示例2: 打开文件并读取内容

    使用Read()函数按照字节读

    package main
    
    import (
    	"fmt"
    	"io"
    	"os"
    )
    
    func main() {
    
    	file, err := os.Open("./test.txt")
    	if err != nil {
    		fmt.Printf("open file failed, err:%v
    ", err)
    		return
    	}
    	defer func() {
    		err = file.Close()
    		if err != nil {
    			fmt.Printf("close file failed, err:%v
    ", err)
    		}
    	}()
    
    	var content []byte
    	var tmp = make([]byte, 128)
    	for {
    		n, err := file.Read(tmp)
    		//为什么是tmp[:n]而不是tmp[:]?
    		//因为当读取到最后一行的内容长度不足tmp的长度的时候
    		//新读取的内容只会覆盖前半部分上次读取到的tmp的内容,
    		//后半部分还是上一次读取的内容,如果用tmp[:]就会导致
    		//后半部分久内容又会被重新赋值一次,这其实是错误的
    		content = append(content, tmp[:n]...)
    		if err == io.EOF {//读到文件末尾
    			break
    		}
    	}
    	fmt.Printf("读取出来的内容为:
    ")
    	fmt.Printf("%q
    ", string(content))
    }
    

    示例3: 一次性读取文件

    读取文件内容并显示在终端,将文件内容一次性读取到终端,适用于文件不大的情况。

    package main
    
    import (
        "fmt"
        "io/ioutil"
    )
    
    func main() {
    
        //打开文件,文件路径相对于GOPATH开始,或者写全路径(/Users/xxx/Go/src/file.txt)
        file, err := ioutil.ReadFile("src/file.txt")
        if err != nil {
            fmt.Println("文件打开失败,原因是:", err)
        }
    
        fmt.Printf("%s", string(file))
    }
    

    示例4: 带缓冲的Reader读文件

    读取文件的内容并显示在终端(带缓冲区的方式),使用os.Open, file.Close,bufio.NewReader,reader.ReadString函数和方法。适合读取大文件
    1.使用ReadBytes方法
    代码1:

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"io"
    	"log"
    	"os"
    )
    
    func main() {
    
    	file, err := os.Open("./test.txt")
    	if err != nil {
    		log.Fatalf("open file failed, err: %v
    ", err)
    	}
    	defer func() {
    		err = file.Close()
    		if err != nil {
    			log.Fatalf("close file failed, err: %v
    ", err)
    		}
    	}()
    
    	//定义变量result用来存储读取结果
    	var result string
    	//创建一个带有缓冲区的reader
    	reader := bufio.NewReader(file)
    	for {
    		buf, err := reader.ReadBytes('
    ')
    		if err != nil && err == io.EOF { //EOF代表文件的末尾
    			//注意:为什么要判断err是否等于io.EOF?
    			//因为存在这种情况,文件有内容的最后那一行尾部没有换行
    			//当使用ReadBytes或者ReadString方法按照'
    '换行读取时,读到尾部没有换行的这种情况时就会报io.EOF错误
    			//此时buf是读取到了内容的,如果忽略掉了,那么最终的读取结果会少了最后一行的内容
    			result += string(buf)
    			break
    		}
    		result += string(buf)
    	}
    	fmt.Println(result)
    }
    

    代码2:

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"io"
    	"log"
    	"os"
    )
    
    func main() {
    
    	file, err := os.Open("./test.txt")
    	if err != nil {
    		log.Fatalf("open file failed, err: %v
    ", err)
    	}
    	defer func() {
    		err = file.Close()
    		if err != nil {
    			log.Fatalf("close file failed, err: %v
    ", err)
    		}
    	}()
    
    	//定义变量result用来存储读取结果
    	var result string
    	//创建一个带有缓冲区的reader
    	reader := bufio.NewReader(file)
    	for {
    		buf, err := reader.ReadBytes('
    ')
    		if err != nil {
    			if err == io.EOF { //EOF代表文件的末尾
    			//注意:为什么要判断err是否等于io.EOF?
    			//因为存在这种情况,文件有内容的最后那一行尾部没有换行
    			//当使用ReadBytes或者ReadString方法按照'
    '换行读取时,读到尾部没有换行的这种情况时就会报io.EOF错误
    			//此时buf是读取到了内容的,如果忽略掉了,那么最终的读取结果会少了最后一行的内容
    				result += string(buf)
    				break
    			} else {
    				log.Fatalf("ReadBytes failed, err: %v
    ", err)
    			}
    		}
    		result += string(buf)
    	}
    	fmt.Println(result)
    }
    

    2.ReadString方法

    package main
    
    import (
        "bufio"
        "fmt"
        "io"
        "os"
    )
    
    func main() {
    
        //打开文件
        file, err := os.Open("./files/test.txt")
        if err != nil {
            fmt.Println("文件打开失败,原因是:", err)
            return
        }
    
        //当函数退出时,要及时的关闭file
        defer func() {
            //文件及时关闭
            err = file.Close()
            if err != nil {
                fmt.Println("文件关闭失败,原因是", err)
            }
        }()
    
        //创建一个 *Reader,是带缓冲的
        reader := bufio.NewReader(file)
        var result string
        //循环读取文件内容
        for {
            str, err := reader.ReadString('
    ') //读到一个换行就结束
            result += str
            if err == io.EOF {//io.EOF代表文件的末尾
                //注意:如果文件最后一行文字没有换行,则会一直读取到文件末尾,
                //所以即使是判断读到了文件末尾,也要把读取的内容输出一下
                break
            }
        }
        fmt.Println(result)
    }
    

    示例5: 创建文件并写入内容

    第二个参数:文件代开模式(可以组合);第三个参数:权限控制(如0755)

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    
    	//1.创建文件file.txt
    	file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE, 0755)
    	if err != nil {
    		fmt.Println("文件打开/创建失败,原因是:", err)
    		return
    	}
    
    	defer func() {
    		err  = file.Close()
    		if err != nil {
    			fmt.Println("文件关闭失败,原因是:", err)
    		}
    	}()
    
    	//写入数据
    	var str = "暗黑西游狮驼岭,斗战胜佛孙悟空。
    "
    
    	for i := 0; i < 5; i++ {
    		file.WriteString(str)
    	}
    }
    

    示例6: 写文件的四种方式

    1.使用WriteAt()搭配Seek()方法实现写文件功能

    package main
    
    import (
    	"io"
    	"log"
    	"os"
    )
    
    func main() {
    
    	file, err := os.OpenFile("./test.txt", os.O_RDWR|os.O_CREATE, 0755)
    	if err != nil {
    		log.Fatalf("open file failed, err: %v
    ", err)
    	}
    	defer func() {
    		err = file.Close()
    		if err != nil {
    			log.Fatalf("close file failed, err: %v
    ", err)
    		}
    	}()
        //Seek(): 修改文件的读写指针位置.
        //参数1: 偏移量. 正:向文件尾部偏移, 负:向文件头部偏移
        //参数2: 偏移起始位置
        //       io.SeekStart: 文件起始位置
        //       io.SeekCurrent: 文件当前位置
        //       io.SeekEnd: 文件结尾位置
        //返回值:表示从文件起始位置,到当前文件读写指针位置的偏移量。
        //WriteAt(): 在文件指定偏移位置,写入[]byte,通常搭配Seek()
        //参数1: 待写入的数据
        //参数2: 偏移量
        //返回: 实际写出的字节数
    	for i := 0; i < 5; i++ {
    		offset, _ := file.Seek(-3, io.SeekEnd)
    		_, _ = file.WriteAt([]byte("你好"), offset)
    	}
    }
    

    注意: 由于使用的OpenFile函数打开的文件,所以在选择打开模式的时候不能选择os.O_APPEND模式,因为该模式表示的是在文件末尾追加,这与WriteAt在指定的位置写是想冲突的,虽然我在测试的时候加上os.O_APPEND模式并没有报错,但是代码执行完之后发现,想要写入的内容并没有真正的写入到文件中。
    写入前

    写入后

    2.一次性写文件

    package main
    
    import (
    	"io/ioutil"
    	"log"
    )
    
    func main() {
    	str := "hello树先生"
    	//如果文件已存在,则会清空原来的内容,写入新内容,如果文件不存在,则会创建文件并写入内容
    	err := ioutil.WriteFile("./test.txt", []byte(str), 0755)
    	if err != nil {
    		log.Fatalf("写入文件错误,错误为:%v
    ", err)
    	}
    }
    

    3.使用带缓冲的方式写文件

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    )
    
    func main() {
    
    	//1.创建文件file.txt
    	file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0755)
    	defer func() {
    		err  = file.Close()
    		if err != nil {
    			fmt.Println("文件关闭失败,原因是:", err)
    		}
    	}()
    
    	if err != nil {
    		fmt.Println("文件创建失败,原因是:", err)
    		return
    	}
    
    	//写入数据
    	var str = "你好,世界
    "
    
    	//写入时,使用带缓存的*Writer
    	writer := bufio.NewWriter(file)
    
    	for i := 0; i < 5; i++ {
    		writer.WriteString(str)
    	}
    
    	//因为writer是带缓存的,因此在调用writeString方法时,其实内容是先写入到缓存
    	//因此需要调用Flush方法,将缓存数据写入到文件中,否则文件中会丢失数据
    	writer.Flush()
    }
    

    示例7: 把一个文件内容写入到另一个文件

    package main
    
    import (
        "fmt"
        "io/ioutil"
    )
    
    func main() {
    
        //打开文件,文件路径相对于GOPATH开始,或者写全路径(/Users/xxx/Go/src/file.txt)
        data, err := ioutil.ReadFile("src/1.txt")
        if err != nil {
            fmt.Println("文件打开失败,原因是:", err)
        }
    
        err = ioutil.WriteFile("src/2.txt", data, 0755)
    
        if err != nil {
            fmt.Println("文件写入失败,原因是:", err)
        }
    }
    

    示例8:使用bufio获取用户输入

    package main
    
    import (
        "bufio"
        "fmt"
        "os"
    )
    
    func main() {
        var s string
        var reader = bufio.NewReader(os.Stdin)
        s, _ = reader.ReadString('
    ')
        fmt.Printf("读取到的内容为:%s
    ", s)
    }
    

    示例9: 判断文件或目录是否存在

    Go判断文件或文件夹是否存在的方法为使用os.Stat()函数返回的错误值进行判断:
    (1)如果返回的错误为nil,说明文件或文件夹存在
    (2)如果返回的类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
    (3)如果返回的错误为其它类型,则不确定是否存在

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    
    	isExist, err := isFileExists("src/sfile.txt")
    	if err != nil {
    		fmt.Println("发生错误:", err)
    	}
    
    	if isExist {
    		fmt.Println("存在")
    	} else {
    		fmt.Println("不存在")
    	}
    }
    
    //判断文件或者目录是否存在
    func isFileExists(path string) (bool, error) {
    
    	_, err := os.Stat(path)
    	if err == nil {
    		return true, nil
    	}
    	if os.IsNotExist(err) {
    		return false, nil
    	}
    	return false, err
    }
    

    示例10: 拷贝文件、图片音视频

    io.Copy方法

    package main
    
    import (
    	"fmt"
    	"io"
    	"os"
    )
    func CopyFile(srcFileName string, dstFileName string) (int64, error) {
    
    	//源文件处理
    	srcFile, err := os.Open(srcFileName)
    	defer func() {
    		err = srcFile.Close()
    		if err != nil {
    			fmt.Println("源文件关闭失败,原因是:", err)
    		}
    	}()
    
    	if err != nil {
    		fmt.Println("源文件打开失败,原因是:", err)
    		return 0, err
    	}
    
    	//目标文件处理
    	dstFile, err := os.OpenFile(dstFileName, os.O_CREATE | os.O_WRONLY, 0755)
    	defer func() {
    		err = dstFile.Close()
    		if err != nil {
    			fmt.Println("目标文件关闭失败,原因是:", err)
    		}
    	}()
    	if err != nil {
    		fmt.Println("目标文件打开失败,原因是:", err)
    		return 0, err
    	}
    
    	return io.Copy(dstFile, srcFile)
    }
    
    func main() {
    
    	result, err := CopyFile("src/dst.jpeg", "src/哈哈.jpeg")
    
    	if err == nil {
    		fmt.Println("拷贝成功!拷贝的字节数为: ", result)
    	}
    }
    

    对于大文件,我们还可以采用下面的方式

    package main
    
    import (
    	"io"
    	"log"
    	"os"
    )
    
    func CopyFile(srcFileName string, dstFileName string) {
    	//打开源文件
    	srcFile, err := os.Open(srcFileName)
    	if err != nil {
    		log.Fatalf("源文件读取失败,原因是:%v
    ", err)
    	}
    	defer func() {
    		err = srcFile.Close()
    		if err != nil {
    			log.Fatalf("源文件关闭失败,原因是:%v
    ", err)
    		}
    	}()
    
    	//创建目标文件,稍后会向这个目标文件写入拷贝内容
    	distFile, err := os.Create(dstFileName)
    	if err != nil {
    		log.Fatalf("目标文件创建失败,原因是:%v
    ", err)
    	}
    	defer func() {
    		err = distFile.Close()
    		if err != nil {
    			log.Fatalf("目标文件关闭失败,原因是:%v
    ", err)
    		}
    	}()
    	//定义指定长度的字节切片,每次最多读取指定长度
    	var tmp = make([]byte, 1024*4)
    	//循环读取并写入
    	for {
    		n, err := srcFile.Read(tmp)
    		n, _ = distFile.Write(tmp[:n])
    		if err != nil {
    			if err == io.EOF {//读到了文件末尾,并且写入完毕,任务完成返回(关闭文件的操作由defer来完成)
    				return
    			} else {
    				log.Fatalf("拷贝过程中发生错误,错误原因为:%v
    ", err)
    			}
    		}
    	}
    }
    
    func main() {
    	CopyFile("./worm.mp4", "./dist.mp4")
    }
    

    示例11: 遍历目录

    遍历目录

    package main
    
    //我们读写的文件一般存放于目录中.因此,有时需要指定到某一个目录下,根据目录存储的状况
    //再进行文件的特定操作.接下来我们看看目录的基本操作方法.
    import (
    	"fmt"
    	"log"
    	"os"
    )
    //打开目录
    //打开目录我们也使用OpenFile函数,但要指定不同的参数来通知系统,要打开的是一个目录文件.
    //func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
    //参数1: name,表示要打开的目录名称.使用绝对路径较多
    //参数2: flag,表示打开文件的读写模式
    //参数3: perm,表示打开权限.但对于目录来说有所不同,通常传os.ModeDir.
    //返回值:由于是操作目录,所以file是指向目录的文件指针.err中保存错误信息
    
    //读目录内容
    //这与读文件有所不同.目录中存放的是文件名和子目录名.所以使用Readdir函数
    //func (f *File) Readdir(n int) (fi []FileInfo, err error)
    //如果n>0,Readdir函数会返回一个最多n个成员的切片。这时,如果Readdir返回一个空切片,
    //它会返回一个非nil的错误说明原因。如果到达了目录f的结尾,返回值err会是io.EOF。
    //
    //如果n<=0,Readdir函数返回目录中剩余所有文件对象的FileInfo构成的切片。
    //此时,如果Readdir调用成功(读取所有内容直到结尾),它会返回该切片和nil的错误值。
    //如果在到达结尾前遇到错误,会返回之前成功读取的FileInfo构成的切片和该错误。
    
    func main() {
    	//不推荐,因为通过查看ioutil.ReadDir()函数可知,官方使用的是os.Open()函数打开的目录
    	//file, err := os.OpenFile("./dir", os.O_RDWR, os.ModeDir)
    	file, err := os.Open("./dir")
    	if err != nil {
    		log.Fatalf("文件打开失败,原因是:%v
    ", err)
    	}
    	defer func() {
    		err = file.Close()
    		if err != nil {
    			log.Fatalf("文件关闭失败,原因是:%v
    ", err)
    		}
    	}()
    	//Readdir方法返回一个FileInfo接口类型的切片和一个error类型的错误
    	infos, err := file.Readdir(-1)
    	for _, info := range infos {
    		fmt.Printf("%v, %v
    ", info.Name(), info.IsDir())
    	}
    }
    

    仅遍历目录,忽略文件

    方法1:使用os包

    package main
    
    import (
        "fmt"
        "os"
    )
    
    var dirNames = make([]string, 0, 50)
    var pathSeparator = string(os.PathSeparator)
    func traverseDir(filePath string) error {
        file, err := os.Open(filePath)
        if err != nil {
            return err
        }
        fileInfo, err := file.Readdir(0)
        if err != nil {
            return err
        }
    
        for _, value := range fileInfo {
            if value.IsDir() {
                dirNames = append(dirNames, value.Name())
                err = traverseDir(filePath+pathSeparator+value.Name())
                if err != nil {
                    return err
                }
            }
        }
        return err
    }
    
    func main() {
    
        var filePath = "./dir"
        err := traverseDir(filePath)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println(dirNames)
    }
    

    方法2:使用ioutil包

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "os"
    )
    
    var dirNames = make([]string, 0, 50)
    var pathSeparator = string(os.PathSeparator)
    func traverseDir(filePath string) error {
        fileInfos, err := ioutil.ReadDir(filePath)
        if err != nil {
            return err
        }
        for _, fileInfo :=range fileInfos {
            if fileInfo.IsDir() {
                dirNames = append(dirNames, fileInfo.Name())
                err =  traverseDir(filePath+pathSeparator+fileInfo.Name())
                if err != nil {
                    return err
                }
            }
        }
        return err
    }
    
    func main() {
    
        var filePath = "./dir"
        err := traverseDir(filePath)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println(dirNames)
    }
    

    示例12: 修改文件名

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "os"
        "strings"
    )
    
    var pathSeparator = string(os.PathSeparator)
    //重命名文件
    func renameFileName(filePath string, old string, new string) error {
        files, err := ioutil.ReadDir(filePath)
        if err != nil {
            return err
        }
        for _, fileInfo := range files {
            if !fileInfo.IsDir() {
                err = os.Rename(filePath + pathSeparator + fileInfo.Name(),
                    filePath + pathSeparator + strings.Replace(fileInfo.Name(), old, new, -1),
                )
                if err != nil {
                    return err
                }
            }
        }
        return err
    }
    
    func main() {
        var filePath = "./dir"
        err := renameFileName(filePath, "f", "kkk")
        if err != nil {
            fmt.Printf("错误: %v
    ", err)
        }
    }
    

    示例13:创建目录

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    	//Mkdir使用指定的权限和名称创建一个目录。如果出错,会返回*PathError底层类型的错误。
    	err := os.Mkdir("./foo", 0755)
    	if os.IsExist(err) {
    		fmt.Println("目录已存在")
    		return
    	}
    
    	//MkdirAll使用指定的权限和名称创建一个目录,包括任何必要的上级目录,并返回nil,否则返回错误。
    	//权限位perm会应用在每一个被本函数创建的目录上。如果path指定了一个已经存在的目录,MkdirAll不做任何操作并返回nil。
    	err = os.MkdirAll("./foo/bar", 0755)
    	if err != nil {
    		fmt.Printf("%v
    ", err)
    		return
    	}
    }
    

    示例14:删除文件

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func main() {
    	//Remove删除name指定的文件或目录。如果出错,会返回*PathError底层类型的错误。
    	//该方法不能删除非空目录,如果想删除目录以及目录下的所有文件,可以使用RemoveAll
    	err := os.Remove("./def")
    	if os.IsNotExist(err) {
    		fmt.Println("您要删除的文件或目录不存在")
    		return
    	}
    	if err != nil {
    		fmt.Println(err)
    	}
    
    	//RemoveAll删除path指定的文件,或目录及它包含的任何下级对象。
    	//它会尝试删除所有东西,除非遇到错误并返回。
    	//如果path指定的对象不存在,RemoveAll会返回nil而不返回错误。
    	err = os.RemoveAll("./def")
    	if err != nil {
    		fmt.Println(err)
    	}
    }
    
  • 相关阅读:
    rem适配布局---5. 方案1:苏宁首页制作1
    rem适配布局---4. rem适配方案
    rem适配布局---3. less
    rem适配布局---2. 媒体查询
    rem适配布局---1. 基础
    flex布局---9.携程网案例
    java基础---3. 数据类型转换、运算符
    flex布局---8.flex布局原理
    java基础---2. 常量&变量
    工会项目结题,游泳锻炼
  • 原文地址:https://www.cnblogs.com/itbsl/p/13052631.html
Copyright © 2020-2023  润新知