Go语言学习之路第9天(文件操作)

Posted dacaigouzi1993

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言学习之路第9天(文件操作)相关的知识,希望对你有一定的参考价值。

一.文件操作

1.1 创建文件

  新建文件可以通过如下方法,Create()方法:

func Create(name string) (file *File, err error)

  Create采用模式0666(任何人都可读写,不可执行,但实际文件权限是由你linux服务器上的umask值决定的)创建一个名为name的文件,如果文件已存在会截断它(为空文件)。如果成功,返回的文件对象可用于I/O;对应的文件描述符具O_RDWR模式。如果出错,错误底层类型是*PathError。

  参数:新创建的文件名;可以绝对路径;也可以相对路径( 相对.go 或 .exe文件。)

  返回值

    file:文件指针。本质:file对象(结构体)

    err:错误返回

  特性

    文件不存在:创建新文件。

    文件存在:覆盖源文件。(截断为0)

  具体实例如下:

import (
	"fmt"
	"os"
)

func main()  
	fw,err := os.Create("./hello.txt")
	if err != nil 
		fmt.Println("os.Create err:",err)
		return
	

	defer fw.Close()

 

1.2 打开文件

  打开文件有两种方式:

  (1)以只读的方式打开文件,Open()方法

func Open(name string) (file *File, err error)

  Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。

  参数:打开的文件名:可以绝对路径;也可以相对路径(相对.go 或 .exe文件。)

  返回值

    file:文件指针。本质:file对象(结构体)

    err:错误返回

  特性

    文件存在:只读打开

    文件不存在:报错返回。

  具体实例如下:

import (
	"fmt"
	"os"
)

func main()  
	fr,err := os.Open("./hello.txt")
	if err != nil
		fmt.Println("os.Open err:",err)
		return
	
	defer fr.Close()

  注意:用Open打开的文件只能用于读取,不能用于写入。

 

  (2)以读写的方式打开文件,OpenFile()方法

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

  OpenFile是一个更一般性的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果操作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。

  参数一:打开的文件名:可以绝对路径;也可以相对路径(相对.go 或 .exe文件。)

  参数二:打开文件的操作权限:

    os.O_RDONLY : 只读打开

    os.O_WRONLY : 只写打开

    os.O_RDWR : 读写打开 。 常用

    os.O_APPEND: 追加写

    os.O_CREATE:创建文件

    这里的权限可以进行组合,用 | 分隔,如:

      os.O_RDWR | os.O_CREATE:创建文件并且以读写的方式打开文件

      os.O_RDWR | os.O_APPEND:以读写的方式打开文件并且以追加的方式写(默认的写是覆盖写)

  参数三

    通常传 0

    当参数二为:O_CREATE时,该参数有实际作用:指定新建文件的属性

      权限取值范围(0-7),表示如下:

        0:没有任何权限

        1:执行权限(如果是可执行文件,是可以运行的)

        2:写权限

        3: 写权限与执行权限

        4:读权限

        5: 读权限与执行权限

        6: 读权限与写权限

        7: 读权限,写权限,执行权限

  返回值:

    file:文件指针。本质:file对象(结构体)

    err:错误返回:

  具体案例如下:

import (
	"fmt"
	"os"
)

func main()  
	//创建文件并且以读写的方式打开文件
	frw,err := os.OpenFile("./hello.txt",os.O_RDWR | os.O_CREATE,0)

	//以读写的方式打开文件并且以追加的方式写
	//frw,err := os.OpenFile("./hello.txt",os.O_RDWR | os.O_APPEND,0)
	if err != nil 
		fmt.Println("os.OpenFile err:",err)
		return
	
	defer frw.Close()

 

1.3 关闭文件

func (f *File) Close() error

  Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。

 

1.4 写文件

  (1)按字符串写,WriteString()方法

func (f *File) WriteString(s string) (ret int, err error)

  这个方式是由以写的模式打开文件而生成的文件对象调用的。

  参数:待写入文件的字符串

  返回值:

    ret:写入文件的字节数

    err:错误返回

  具体实例如下:

func main()  
	fw,err := os.Create("./hello.txt")
	if err != nil
		fmt.Println("os.Create err:",err)
		return
	
     defer fw.Close() n,err := fw.WriteString("Hello World\n") if err != nil fmt.Println("fw.WriteString err:",err) return fmt.Println(n)
      

  执行之后,hello.txt文件中内容如下:

Hello World

 

  (2)按位置写

  先确定位置,Seek()方法:

func (f *File) Seek(offset int64, whence int) (ret int64, err error)

  Seek设置下一次读/写的位置。offset为相对偏移量,而whence决定相对位置:0为相对文件开头,1为相对当前位置,2为相对文件结尾。它返回新的偏移量(相对开头)和可能的错误。

  参数一:偏移量;矢量:正数向后,负数向前偏。

  参数二:

    0或者io.SeekStart : 从文件起始位置开始偏移。

    1或者io.SeekCurrent: 从当前位置。

    2或者io.SeekEnd: 从文件末尾位置开始偏移。

  返回值:

    ret:较文件起始位置,偏过的字节数。

    err:错误返回

 

  然后根据的得到的偏移量来写文件,WriteAt()方法:

func (f *File) WriteAt(b []byte, off int64) (n int, err error)

  WriteAt在指定的位置(相对于文件开始位置)写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。

  参数一:待写入文件的数据内容,是字符切片类型。

  参数二:偏移量;Seek函数的返回值ret。

  返回值:

    n:写入文件的字节

    err:错误返回

  具体实例如下:

func main()  
	fw,err := os.OpenFile("./hello.txt",os.O_RDWR,0)
		if err != nil
			fmt.Println("os.OpenFile err:",err)
			return
	
	defer fw.Close()

	//位置偏移到末尾
	ret,err := fw.Seek(0,io.SeekEnd)
	if err != nil
		fmt.Println("fw.Seek err:",err)
		return
	

	n,err := fw.WriteAt([]byte("This is a Go Program\n"),ret)
	if err != nil
		fmt.Println("fw.WriteAt err:",err)
		return
	

	fmt.Println(n)

  执行完后hello.txt中的内容是:

Hello World
This is a Go Program

 

  (3)按字符写,Write()方法

func (f *File) Write(b []byte) (n int, err error)

  Write向文件中写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。

  参数:待写入文件的数据内容

  返回值:

    n:写入的字节个数

    err:错误处理

  具体实例如下:

func main()  
	//以读写方式打开文件并以追加的方式写
	fw,err := os.OpenFile("./hello.txt",os.O_RDWR | os.O_APPEND,0)
		if err != nil
			fmt.Println("os.OpenFile err:",err)
			return
	
	defer fw.Close()

	n,err := fw.Write([]byte("Good Good Study,Day Day Up\n"))
	if err != nil
		fmt.Println("fw.Write err:",err)
		return
	
	fmt.Println(n)


  执行完后,hello.txt文件中内容如下:

Hello World
This is a Go Program
Good Good Study,Day Day Up

 

1.5 读文件

  (1)按行读文件

  可以通过两步来实现。

  首先,创建一个带有缓冲区的Reader,使用bufio包下的NewReader()函数来实现。

func NewReader(rd io.Reader) *Reader

   NewReader创建一个具有默认大小缓冲、从r读取的*Reader。

  参数:文件指针(open、Create、OpenFile 返回值)

  返回值:带有缓冲区的阅读器

 

  然后,从Reader 自带的缓冲区中,按指定的“分隔符(delim)”获取数据。我们通常使用ReadBytes(‘\n’) 来有效的提取一行数据。

func (b *Reader) ReadBytes(delim byte) (line []byte, err error)

  ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。

  读到的数据成功保存在返回的[]byte 中。可以循环按行读取整个文件。

  参数:分隔符,一般都是‘\n‘,Linux系统中的行结束标记。

  返回值:

    line:实际读到的一行数据,是字符切片类型

    err:错误处理

  文件结尾:EOF —— end of file

    当读到文件末尾时,err 会被置为 io.EOF

  具体实例如下:

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

func main()  
	fr,err := os.Open("./hello.txt")
	if err != nil
		fmt.Println("os.Open err:",err)
		return
	

	defer fr.Close()

	reader := bufio.NewReader(fr)
	for
		line,err := reader.ReadBytes(‘\n‘)
		if err != nil
			if err == io.EOF
				fmt.Println("文件已读完")
				break
			else 
				fmt.Println("reader.ReadBytes err:",err)
				return
			
		
		fmt.Print(string(line))
	

  结果如下:

Hello World
This is a Go Program
Good Good Study,Day Day Up
文件已读完

 

  (2)按字节读,Read()方法

func (b *Reader) Read(p []byte) (n int, err error)

  Read读取数据写入p。本方法返回写入p的字节数。本方法一次调用最多会调用下层Reader接口一次Read方法,因此返回值n可能小于len(p)。读取到达结尾时,返回值n将为0而err将为io.EOF。

  参数:空缓冲区(需要自己创建),用来存放read读到的数据内容。

  返回值:

    n:实际读到的字节个数

    err:错误处理

  具体实例如下:

func main()  
	fr,err := os.Open("./hello.txt")
	if err != nil
		fmt.Println("os.Open err:",err)
		return
	
	defer fr.Close()

	//自定义缓冲区
	slice := make([]byte,4096)

	for
		n,err := fr.Read(slice)
		if err != nil
			if err == io.EOF
				fmt.Println("文件已读完")
				break
			else 
				fmt.Println("fr.Read err:",err)
				return
			
		
		fmt.Print(string(slice[:n]))
	

  得到结果如下:

Hello World
This is a Go Program
Good Good Study,Day Day Up
文件已读完

 

1.6 大文件拷贝

  文件拷贝的核心思想就是读多少就写多少

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

func main()  
	//以只读的方式打开待源文件
	fr,err := os.Open("./01-复习.avi")
	if err != nil
		fmt.Println("os.Open err:",err)
		return
	
	defer fr.Close()

	//以写的方式打开目标文件
	fw,err := os.Create("./复习.avi")
	if err != nil
		fmt.Println("os.Create err:",err)
		return
	

	//创建缓冲区
	buf := make([]byte,4096)

	//循环读取源文件内容,然后读取多少就往目标文件写多少
	for
		n,err := fr.Read(buf)
		if err != nil
			if err == io.EOF
				fmt.Println("文件以拷贝完毕")
				break
			else 
				fmt.Println("fr.Read err:",err)
				return
			
		
		_,err = fw.Write(buf[:n])
		if err != nil
			fmt.Println("fw.Write err:",err)
			return
		
	


 

二.目录操作 

我们读写的文件一般存放于目录中。因此,有时需要指定到某一个目录下,根据目录存储的状况再进行文件的特定操作。接下来我们看看目录的基本操作方法。

(1) 打开目录,OpenFile()方法

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

参数一:绝对或相对路径名

参数二:O_RDONLY

参数三:os.ModeDir

返回值:

  file:指向目录的文件指针

  err:错误处理

 

(2)读目录内容,Readdir()方法

这与读文件有所不同。目录中存放的是文件名和子目录名。所以使用Readdir函数来完成。

func (f *File) Readdir(n int) (fi []FileInfo, err error)

  Readdir读取目录f的内容,返回一个有n个成员的[]FileInfo,这些FileInfo是被Lstat返回的,采用目录顺序。对本函数的下一次调用会返回上一次调用剩余未读取的内容的信息。

  如果n>0,Readdir函数会返回一个最多n个成员的切片。这时,如果Readdir返回一个空切片,它会返回一个非nil的错误说明原因。如果到达了目录f的结尾,返回值err会是io.EOF。

  如果n<=0,Readdir函数返回目录中剩余所有文件对象的FileInfo构成的切片。此时,如果Readdir调用成功(读取所有内容直到结尾),它会返回该切片和nil的错误值。如果在到达结尾前遇到错误,会返回之前成功读取的FileInfo构成的切片和该错误。

参数:取的目录项个数, -1 表示所有。

返回值:

  fi:FileInfo类型的切片。其内部保存了文件名。

  err:错误信息。

type FileInfo interface 
    Name() string       // 文件的名字(不含扩展名)
    Size() int64        // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同
    Mode() FileMode     // 文件的模式位
    ModTime() time.Time // 文件的修改时间
    IsDir() bool        // 等价于Mode().IsDir()
    Sys() interface   // 底层数据来源(可以返回nil)

得到FileInfo类型切片后,我们可以range遍历切片元素,使用.Name()获取文件名。使用.Size()获取文件大小,使用.IsDir()判断文件是目录还是非目录文件。

如:我们可以提示用户提供一个目录位置,打开该目录,查看目录下的所有成员,并判别他们是文件还是目录。

示例如下:

import (
	"fmt"
	"os"
)

func main()  
	fmt.Print("请输入要找寻的目录:")
	var path string
	fmt.Scan(&path)

	dir,err := os.OpenFile(path,os.O_RDONLY,os.ModeDir)
	if err != nil
		fmt.Println("os.OpenFile err:",err)
		return
	

	s,err := dir.Readdir(-1)
	if err != nil
		fmt.Println("dir.Readdir:",err)
		return
	

	for _,data := range s
		if data.IsDir()
			fmt.Println(data.Name(),"是一个目录。")
		else 
			fmt.Println(data.Name(),"是一个文件。")
		
	

 

 

 

 

 

 

 
 
 
 

 

以上是关于Go语言学习之路第9天(文件操作)的主要内容,如果未能解决你的问题,请参考以下文章

Go 事,如何成为一个Gopher ,并在7天找到 Go 语言相关工作,第1篇

go语言中的递归函数

Go 事,如何成为一个Gopher ,并在7天找到 Go 语言相关工作,第1篇

Go 事,如何成为一个Gopher ,并在7天找到 Go 语言相关工作,第1篇

七天入门Go语言 文件 && 包 | 第五天 渐入佳境

《Go语言精进之路,从新手到高手的编程思想方法和技巧1》读书笔记和分享