一.文件操作
1.1 创建文件
新建文件可以通过如下方法,Create()方法:
func Create(name string) (file *File, err error)
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。
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_RDWR | os.O_CREATE:创建文件并且以读写的方式打开文件
os.O_RDWR | os.O_APPEND:以读写的方式打开文件并且以追加的方式写(默认的写是覆盖写)
参数三:
当参数二为: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)
这个方式是由以写的模式打开文件而生成的文件对象调用的。
参数:待写入文件的字符串
返回值:
具体实例如下:
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 ") 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为相对文件结尾。它返回新的偏移量(相对开头)和可能的错误。
参数一:偏移量;矢量:正数向后,负数向前偏。
参数二:
1或者io.SeekCurrent: 从当前位置。
2或者io.SeekEnd: 从文件末尾位置开始偏移。
返回值:
然后根据的得到的偏移量来写文件,WriteAt()方法:
func (f *File) WriteAt(b []byte, off int64) (n int, err error)
WriteAt在指定的位置(相对于文件开始位置)写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。
参数一:待写入文件的数据内容,是字符切片类型。
参数二:偏移量;Seek函数的返回值ret。
返回值:
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 "),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 ")) 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(‘ ’) 来有效的提取一行数据。
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。
读到的数据成功保存在返回的[]byte 中。可以循环按行读取整个文件。
参数:分隔符,一般都是' ',Linux系统中的行结束标记。
返回值:
err:错误处理
当读到文件末尾时,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(' ') 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读到的数据内容。
返回值:
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(),"是一个文件。") } } }