目录
1.学习内容
1.file文件操作
1.Fileinfo接口
2.权限
3.打开模式
4.File操作
3.I/O操作
1.io包读取文件
2.io包写文件
3.复制文件
4.断点续传
5.bufio包
6.ioutil包
7.遍历文件夹
2.总结
1.学习内容
1.file文件操作
首先,file类是在os包中的,封装了底层的文件描述符和相关信息
1.Fileinfo接口
接口中定义了File信息相关的方法。
type FileInfo interface {
Name() string // 文件的名字(不含扩展名)
Size() int64 // 普通文件返回值表示其大小;其他文件的返回值含义各系统不同
Mode() FileMode // 文件的模式位
ModTime() time.Time // 文件的修改时间
IsDir() bool // 等价于Mode().IsDir()
Sys() interface{} // 底层数据来源(可以返回nil)
}
package main
import (
"fmt"
"os"
)
func main() {
//Fileinfo:文件信息
file1, err := os.Stat("HighPart/FILE/a.test")
if err != nil {
fmt.Println(err)
return
}
fmt.Println("%T", file1)
//获取文件名
fmt.Println(file1.Name())
//文件大小
fmt.Println(file1.Size())
//是否是目录
fmt.Println(file1.IsDir()) //IsDirectory
//修改时间
fmt.Println(file1.ModTime())
//权限
fmt.Println(file1.Mode())
//底层数据来源
fmt.Println(file1.Sys())
}
运行显示
2.权限
至于操作权限perm,除非创建文件时才需要指定,不需要创建新文件时可以将其设定为0。可以用常量,也可以用数字,具体含义和Unix系统一致。
3.打开模式
文本打开模式:
4.File操作
package main
import (
"fmt"
"os"
"path"
"path/filepath"
)
func main() {
/*文件操作
1.路径:
相对路径:relative
绝对路径:absolute
.当前目录
..上一层
*/
//路径
filename1 := "D:\\gocode\\HighPart\\FILE\\a"
filename2 := "HighPart/FILE/a"
//filepath.IsAbs()用来判断一个文件是不是绝对路径
fmt.Println(filepath.IsAbs(filename1))
fmt.Println(filepath.IsAbs(filename2))
//filepath.Abs()用来返回绝对路径
fmt.Println(filepath.Abs(filename1))
fmt.Println(filepath.Abs(filename2))
fmt.Println("获取父目录:", path.Join("."))
//创建一层文件
err := os.Mkdir("D:\\gocode\\HighPart\\FILE\\b", os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("文件夹创建成功")
//多重文件夹创建
os.MkdirAll("D:\\gocode\\HighPart\\FILE\\a",os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("多重文件夹创建成功")
//创建文件:Creat 0666
file1, err := os.Create("D:\\gocode\\HighPart\\FILE\\b.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(file1.Name())
fmt.Println("文件创建成功")
file2, err := os.Create(filename2) //创建相对路径的文件,以工程为参照的
if err != nil {
fmt.Println(err)
return
}
fmt.Println(file2)
//打开文件:让当前的程序与指定的文件之间建立连接
//简单打开
file3, err := os.Open(filename1)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(file3)
//按照指定的权限打开
file4, err := os.OpenFile(filename1, os.O_RDONLY|os.O_WRONLY, os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(file4)
//关闭文件
//也可以利用defer 进行延迟关闭
//defer file3.Close()
//defer file4.Close()
file3.Close()
file4.Close()
//删除文件或文件夹
//使用os.Remove()是目录的话只能删除空的
//要不然可以使用os.RemoveAll(),需要慎重使用
//还有就是如果使用代码进行删除的话,不经过回收站
err = os.Remove("D:\\gocode\\HighPart\\FILE\\b")
if err != nil {
fmt.Println(err)
return
}
fmt.Println("删除文件成功")
}
3.I/O操作
IO操作就是输入输出操作,用于读写数据,有些语言也被叫做流操作,是指数据通信的通道
GOlang标准库对IO的抽象非常精巧,各个组件可以随意组合,可以作为接口设计的典范
1.io包读取文件
io包提供了对I/O原语的基本接口。本包的基本任务是包装这些原语已有的实现(如os包里的原语),使之成为共享的公共接口,这些公共接口抽象出了泛用的函数并附加了一些相关的原语的操作。
因为这些接口和原语是对底层实现完全不同的低水平操作的包装,除非得到其它方面的通知,客户端不应假设它们是并发执行安全的。
Reader接口
type Reader interface {
Read(p []byte) (n int, err error)
}
Reader接口用于包装基本的读取方法。
Read方法读取len(p)字节数据写入p。它返回写入的字节数和遇到的任何错误。即使Read方法返回值n < len(p),本方法在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len(p)字节,Read按惯例会返回可以读取到的数据,而不是等待更多数据。
当Read在读取n > 0个字节后遭遇错误或者到达文件结尾时,会返回读取的字节数。它可能会在该次调用返回一个非nil的错误,或者在下一次调用时返回0和该错误。一个常见的例子,Reader接口会在输入流的结尾返回非0的字节数,返回值err == EOF或err == nil。但不管怎样,下一次Read调用必然返回(0, EOF)。调用者应该总是先处理读取的n > 0字节再处理错误值。这么做可以正确的处理发生在读取部分数据后的I/O错误,也能正确处理EOF事件。
如果Read的某个实现返回0字节数和nil错误值,表示被阻碍;调用者应该将这种情况视为未进行操作。
package main
import (
"fmt"
"io"
"os"
)
func main() {
/*
读取文件:
Reader接口
*/
//打开文件
fmt.Println("123456")
filename1 := "D:\\gocode\\HighPart\\FILE\\a.test"
file1, err := os.Open(filename1)
if err != nil {
fmt.Println(err)
return
}
//关闭文件
defer file1.Close()
//读取数据
b := make([]byte, 8)
for { //多次进行读取文件
n, err := file1.Read(b)
if n == 0 || err == io.EOF {
fmt.Println()
break
} else {
fmt.Println(n)
//这个会打印出来所有,所以我们需要转换成字符串
fmt.Println(b)
fmt.Println(string(b))
}
}
}
2.io包写文件
write接口
type Writer interface {
Write(p []byte) (n int, err error)
}
Writer接口用于包装基本的写入方法。
Write方法len(p) 字节数据从p写入底层的数据流。它会返回写入的字节数(0 <= n <= len(p))和遇到的任何导致写入提取结束的错误。Write必须返回非nil的错误,如果它返回的 n < len(p)。Write不能修改切片p中的数据,即使临时修改也不行。
package main
import (
"fmt"
"os"
)
func main() {
//写数据
filename1 := "D:\\gocode\\HighPart\\FILE\\b.txt"
//打开文件
//这里我们需要选择模式打开文件
file1, err := os.OpenFile(filename1, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
fmt.Println(err)
return
}
//文件关闭
defer file1.Close()
a := "西安邮电大学\n"
_, err = file1.WriteString(a)
if err != nil {
fmt.Println("write", err)
return
}
b := []byte{'a', 'e', 'b'}
_, err = file1.Write(b)
if err != nil {
fmt.Println("write", err)
return
}
}
3.复制文件
思路:我们可以利用读写操作进行复制文件
所以我们是有三种复制方法
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
)
func Copy(filename1, filename2 string) {
file1, err := os.Open(filename1)
if err != nil {
fmt.Println("open", err)
return
}
defer file1.Close()
file2, err := os.OpenFile(filename2, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("open", err)
return
}
defer file2.Close()
a := make([]byte, 5)
for {
n, err := file1.Read(a)
if n == 0 || err == io.EOF {
fmt.Println("拷贝完毕")
return
} else if err != nil {
fmt.Println("read", err)
}
_, err = file2.Write(a[:n])
if err != nil {
fmt.Println("write", err)
return
}
}
}
// 利用函数直接进行拷贝
func Copy2(filename1, filename2 string) (int64, error) {
file1, err := os.Open(filename1)
if err != nil {
fmt.Println("open", err)
return 0, err
}
defer file1.Close()
file2, err := os.OpenFile(filename2, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("open", err)
return 0, err
}
defer file2.Close()
return io.Copy(file2, file1)
}
// 利用ioutil包下面的函数
func Copy3(filename1, filename2 string) (int, error) {
b, err := ioutil.ReadFile(filename1)
if err != nil {
return 0, err
}
err = ioutil.WriteFile(filename2, b, 0666)
if err != nil {
return 0, err
}
return len(b), nil
}
func main() {
filename1 := "C:\\Users\\Lenovo\\Desktop\\OIP-C.jpg"
filename2 := "D:\\gocode\\HighPart\\FILE\\k.jpg"
//我们自己定义一个函数进行拷贝
//Copy(filename1, filename2)
//n, err := Copy2(filename1, filename2)
n, err := Copy3(filename1, filename2)
fmt.Println(n)
fmt.Println(err)
}
接下来看看源码
func Copy
func Copy(dst Writer, src Reader) (written int64, err error)
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。
func CopyN
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
从src拷贝n个字节数据到dst,直到在src上到达EOF或发生错误。返回复制的字节数和遇到的第一个错误。
只有err为nil时,written才会等于n。如果dst实现了ReaderFrom接口,本函数很调用它实现拷贝。
func ReadFile
func ReadFile(filename string) ([]byte, error)
ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误。
func WriteFile
func WriteFile(filename string, data []byte, perm os.FileMode) error
函数向filename指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。
4.断点续传
type Seeker
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
Seeker接口用于包装基本的移位方法。
Seek方法设定下一次读写的位置:偏移量为offset,校准点由whence确定:0表示相对于文件起始;1表示相对于当前位置;2表示相对于文件结尾。Seek方法返回新的位置以及可能遇到的错误。
移动到一个绝对偏移量为负数的位置会导致错误。移动到任何偏移量为正数的位置都是合法的,但其下一次I/O操作的具体行为则要看底层的实现。
package main
import (
"fmt"
"os"
)
func main() {
//使用seek
filename1 := "D:\\gocode\\HighPart\\FILE\\a.test"
file1, err := os.OpenFile(filename1, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
fmt.Println("open", err)
return
}
b := make([]byte, 10)
file1.Read(b)
fmt.Println(string(b))
file1.Seek(4, os.SEEK_SET)
file1.Read(b)
fmt.Println(string(b))
}
package main
import (
"fmt"
"io"
"os"
"strconv"
"strings"
)
func main() {
//断电续传
filename1 := "C:\\Users\\Lenovo\\Desktop\\OIP-C.jpg"
filename := filename1[strings.LastIndex(filename1, "/")+1:]
filename2 := filename + "q.txt"
file1, err := os.Open(filename1)
if err != nil {
fmt.Println("open", err)
}
file2, err := os.OpenFile(filename2, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("open", err)
}
file3, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
fmt.Println("open", err)
}
defer file1.Close()
defer file2.Close()
defer file3.Close()
//先读取零时文件中的数据,在seek
file3.Seek(0, io.SeekStart)
b := make([]byte, 1024)
n1, err := file3.Read(b)
if err != nil {
fmt.Println("read")
return
}
countstr := string(b[:n1])
count, err := strconv.ParseInt(countstr, 10, 64)
//if err != nil {
// fmt.Println(err)
// return
//}
fmt.Println(count)
//设置读写的位置
file1.Seek(count, io.SeekStart)
file2.Seek(count, io.SeekStart)
date := make([]byte, 1024)
n2 := -1 //读取文件位置
n3 := -1 //写出文件位置
total := int(count) //读取的总量
//复制文件
for {
n2, err = file1.Read(date)
if err == io.EOF || n2 == 0 {
fmt.Println("文件复制")
break
}
n3, err = file2.Write(date[:n2])
total += n3
//将复制的总量,储存到文件中
file3.Seek(0, io.SeekStart)
file3.WriteString(strconv.Itoa(total))
fmt.Println(total)
//if total > 80 {
// panic("断电")
//}
}
}
5.bufio包
bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。
func (*Reader) ReadLine
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
ReadLine是一个低水平的行数据读取原语。大多数调用者应使用ReadBytes('\n')或ReadString('\n')代替,或者使用Scanner。
ReadLine尝试返回一行数据,不包括行尾标志的字节。如果行太长超过了缓冲,返回值isPrefix会被设为true,并返回行的前面一部分。该行剩下的部分将在之后的调用中返回。返回值isPrefix会在返回该行最后一个片段时才设为false。返回切片是缓冲的子切片,只在下一次读取操作之前有效。ReadLine要么返回一个非nil的line,要么返回一个非nil的err,两个返回值至少一个非nil。
返回的文本不包含行尾的标志字节("\r\n"或"\n")。如果输入流结束时没有行尾标志字节,方法不会出错,也不会指出这一情况。在调用ReadLine之后调用UnreadByte会总是吐出最后一个读取的字节(很可能是该行的行尾标志字节),即使该字节不是ReadLine返回值的一部分。
func (*Reader) ReadBytes
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。
func (*Reader) ReadString
func (b *Reader) ReadString(delim byte) (line string, err error)
ReadString读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的字符串。如果ReadString方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadString方法返回的切片不以delim结尾时,会返回一个非nil的错误。
package pake
import (
"bufio"
"fmt"
"os"
)
func Demo01() {
//使用bufio:高效读写
filename01 := "D:\\gocode\\HighPart\\pake\\a"
file, err := os.Open(filename01)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
//创建Reader对象
//b1 := bufio.NewReader(file)
高效读取Read()
//p := make([]byte, 1024)
//n1, err := b1.Read(p)
//fmt.Println(n1)
//fmt.Println(string(p[:n1]))
//ReadLine()
//for {
// data, _, err := b1.ReadLine()
// if err == io.EOF {
// fmt.Println("打印完毕")
// break
// }
// if err != nil {
// fmt.Println("read()", err)
// return
// }
// //fmt.Println(flag)
// fmt.Println(string(data))
//}
//data, flag, err := b1.ReadLine()
//fmt.Println(flag)
//fmt.Println(err)
//fmt.Println(data)
//fmt.Println(string(data))
//ReadString()
//s1, err := b1.ReadString('\n')
//fmt.Println(err)
//fmt.Println(s1)
//s1, err = b1.ReadString('\n')
//fmt.Println(err)
//fmt.Println(s1)
//s1, err = b1.ReadString('\n')
//fmt.Println(err)
//fmt.Println(s1)
//for {
// s1, err := b1.ReadString('\n')
// if err == io.EOF {
// break
// }
// fmt.Println(s1)
//}
//ReadBytes()
//data, err := b1.ReadBytes('\n')
//fmt.Println(err)
//fmt.Println(string(data))
//进行带有空格的读
s2 := ""
b2 := bufio.NewReader(os.Stdin)
s2, _ = b2.ReadString('\n')
fmt.Println(s2)
}
func (*Writer) Write
func (b *Writer) Write(p []byte) (nn int, err error)
Write将p的内容写入缓冲。返回写入的字节数。如果返回值nn < len(p),还会返回一个错误说明原因。
package pake
import (
"bufio"
"fmt"
"os"
)
func Demo02() {
//写文件
filename01 := "D:\\gocode\\HighPart\\pake\\b"
file01, err := os.OpenFile(filename01, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
fmt.Println(err)
return
}
defer file01.Close()
w1 := bufio.NewWriter(file01)
//n, err := w1.WriteString("掌握噶如果")
//fmt.Println(err)
//fmt.Println(n)
//w1.Flush() //刷新缓存区
for i := 1; i <= 10; i++ {
w1.WriteString(fmt.Sprintf("%d,乐此不起\n", i))
}
w1.Flush()
}
6.ioutil包
func ReadAll
func ReadAll(r io.Reader) ([]byte, error)
ReadAll从r读取数据直到EOF或遇到error,返回读取的数据和遇到的错误。成功的调用返回的err为nil而非EOF。因为本函数定义为读取r直到EOF,它不会将读取返回的EOF视为应报告的错误。
func ReadFile
func ReadFile(filename string) ([]byte, error)
ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误。
func WriteFile
func WriteFile(filename string, data []byte, perm os.FileMode) error
函数向filename指定的文件中写入数据。如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。
func ReadDir
func ReadDir(dirname string) ([]os.FileInfo, error)
返回dirname指定的目录的目录信息的有序列表。
func TempDir
func TempDir(dir, prefix string) (name string, err error)
在dir目录里创建一个新的、使用prfix作为前缀的临时文件夹,并返回文件夹的路径。如果dir是空字符串,TempDir使用默认用于临时文件的目录(参见os.TempDir函数)。 不同程序同时调用该函数会创建不同的临时目录,调用本函数的程序有责任在不需要临时文件夹时摧毁它。
func TempFile
func TempFile(dir, prefix string) (f *os.File, err error)
在dir目录下创建一个新的、使用prefix为前缀的临时文件,以读写模式打开该文件并返回os.File指针。如果dir是空字符串,TempFile使用默认用于临时文件的目录(参见os.TempDir函数)。不同程序同时调用该函数会创建不同的临时文件,调用本函数的程序有责任在不需要临时文件时摧毁它。
package pake
func Demo04() {
//ioutil包
//读取文件中的所有数据,不需要开关文件
//filename := "D:\\gocode\\HighPart\\pake\\b"
//data, err := ioutil.ReadFile(filename)
//fmt.Println(err)
//fmt.Println(data)
//fmt.Println(string(data))
//写文件,没有的话会自动创建,有的话会清空
//filename := "D:\\gocode\\HighPart\\pake\\b"
//s1 := "刚发给甲方返回,官方电话"
//err := ioutil.WriteFile(filename, []byte(s1), 0600)
//fmt.Println(err)
//ReadAll()
//s2 := "fggf 带帅哥人噶韩国"
//r1 := strings.NewReader(s2)
//data, err := ioutil.ReadAll(r1)
//fmt.Println(err)
//fmt.Println(data)
//fmt.Println(string(data))
//读取一层文件的目录
//dirName := "D:\\gocode\\HighPart\\pake"
//filename, err := ioutil.ReadDir(dirName)
//fmt.Println(err)
fmt.Println(filename)
//fmt.Printf("%T\n", filename)
//for i := 0; i < len(filename); i++ {
// fmt.Printf("%d %s %t\n", i, filename[i].Name(), filename[i].IsDir())
//}
//创建临时文件和临时目录
//ioutil.TempDir()
//ioutil.ReadFile()
//需要进行删除
}
7.遍历文件夹
之前我们看了一下可以打印文件下目录的函数ioutil.ReadDir(),但是这个函数只能打印一层的文件,所以我们可以利用递归,进行多层的打印,下面就是算法的实现。
package pake
import (
"fmt"
"io/ioutil"
"log"
)
func Demo03() {
dirname := "D:/gocode/HighPart"
listFile(dirname, 0)
}
func listFile(dirname string, level int) {
//level用来记录当前递归的层次,生成带有层次感的空格
s := "|--"
for i := 0; i < level; i++ {
s = "| " + s
}
filename, err := ioutil.ReadDir(dirname)
if err != nil {
log.Fatal(err)
}
for _, fi := range filename {
filename1 := dirname + "/" + fi.Name()
fmt.Printf("%s%s\n", s, filename1)
if fi.IsDir() {
listFile(filename1, level+1)
}
}
}
2.总结
一周挤出来点时间,对一部分内容进行系统的总结,对学习有着很大的提升。