go语言中的io操作主要学习目标
- 掌握文件的常规操作
- 掌握ioutil包的使用
- 掌握bufio包的使用
在go中使用 FileInfo接口 定义了IO的一些函数
FileInfo接口
源码追溯
//type.go
// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo = fs.FileInfo
//fs.go
// A FileInfo describes a file and is returned by Stat. --描述为文件 返回其状态
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others
Mode() FileMode // file mode bits 文件位数
ModTime() time.Time // modification time 修改时间
IsDir() bool // abbreviation for Mode().IsDir() 是否是目录
Sys() any // underlying data source (can return nil)
}
代码Demo
//FileInfo接口
//os包下函数返回
func PrintFileInfo(filepath string){
fileInfo, err := os.Stat(filepath)
if err!=nil{
fmt.Println("err--",err.Error())
}else{
fmt.Printf("数据的类型--%T\n",fileInfo)
fmt.Println(fileInfo.Name())
fmt.Println(fileInfo.Size())
fmt.Println(fileInfo.IsDir())
fmt.Println(fileInfo.Mode())//权限
fmt.Println(fileInfo.ModTime())//最后修改时间
}
}
func main() {
path:="D:/main.go"
PrintFileInfo(path)
}
filepath包下API
我们来看path/filepath包下的常用api
代码Demo
func main() {
path := "D:/main.go"
defer PrintFileInfo(path)
println("-----------分隔符--------------")
defer fileR(path)
}
// 通过文件路径字符串 来判断 文件相关信息
func fileR(fpath string) {
abs, err := filepath.Abs(fpath)
fmt.Printf("文件绝对路径--%s \n", abs) //D:\main.go
dir := filepath.Dir(fpath)
fmt.Printf("文件所在路径--%s \n", dir) //D:\
/*
Base 返回路径的最后一个元素。
在提取最后一个元素之前,将删除尾随路径分隔符。
如果路径为空,则 Base 返回“.”。
如果路径完全由分隔符组成,则 Base 返回单个分隔符。
*/
base := filepath.Base(fpath) //Base 返回路径的最后一个元素
println("如果是目录名", filepath.Base("D:\\")) //如果是目录名 则返回 \
fmt.Printf("路径最后一个元素--%s \n", base) // main.go --实际就是文件名
println("最短路径", filepath.Clean(fpath)) // Clean返回等价于path的最短路径名
println("最短路径", filepath.Clean("D:\\Program Data\\IdeaProject\\smart-admin\\README.md")) // Clean返回等价于path的最短路径名
println(filepath.Join("D:/", "main.go")) //路径拼接 D:\main.go
split, file := filepath.Split(fpath) //将其拆分为目录和文件名
fmt.Printf("该文件所在的目录:%s 文件名:%s \n", split, file)
split1, file2 := filepath.Split("D:\\Program Data\\IdeaProject\\smart-admin\\README.md") //将其拆分为目录和文件名组件
fmt.Printf("该文件所在的目录:%s 文件名:%s \n", split1, file2) //D:\Program Data\IdeaProject\smart-admin\ 文件名:README.md
match, err := filepath.Match("D:/main.go", fpath)
fmt.Printf("文件是否符合正则要求:%t \n", match)
prefix := filepath.HasPrefix(fpath, "D")
fmt.Printf("文件路径%t D 开头\n", prefix)
/*
// EvalSymlinks返回任何符号求值后的路径名链接。
//如果path是相对的,结果将是相对于当前目录的;
//除非其中一个组件是绝对符号链接
*/
symlinks, err := filepath.EvalSymlinks(fpath)
symlinks1, err := filepath.EvalSymlinks("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Adobe Photoshop 2023.ink")
fmt.Printf("求值后的路径名1%s \n", symlinks)
fmt.Printf("求值后的路径名2%s \n", symlinks1)
fmt.Println(err.Error())
println("文件的扩展名:", filepath.Ext(fpath))
println("替换\\后的结果", filepath.FromSlash(fpath))
// Glob返回所有匹配pattern或nil的文件名
glob, err := filepath.Glob("*")//查找文件
for i, s := range glob {
fmt.Println("Glob---->>", i, "---", s)
}
//D:\ChwenyuLim\jdk-11doc\docs\specs\rmi
glob1, err := filepath.Glob("D:\\ChwenyuLim\\jdk-11doc\\docs\\*\\rmi")//查找文件夹 rmi所在路径
for i, s := range glob1 {
fmt.Println("Glob1---->>", i, "---", s)
}
/*
IsLocal仅使用词法分析报告path是否具有以下所有属性:
// -在评估路径所在目录的子树中
// -不是绝对路径
// -不为空
// -在Windows上,不是一个保留名称,如"NUL"
*/
println("是本地文件吗?", filepath.IsLocal(fpath))//false
println("分割符号1", string(filepath.ListSeparator)) // 返回;
println("分割符号2", string(filepath.Separator))// 返回\
//返回相对路径
rel, err := filepath.Rel("D:\\", "D:"+string(filepath.Separator)+"main.go")
fmt.Println("相对路径:",rel)//main.go
list := filepath.SplitList(fpath)
//环境变量切割
list1 := filepath.SplitList("D:\\Program Files (x86)\\VMware\\VMware Workstation\\bin\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Windows\\System32\\OpenSSH\\;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\Program Files\\NVIDIA Corporation\\NVIDIA NvDLISR;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\WINDOWS\\System32\\OpenSSH\\;D:\\Program Files (x86)\\NetSarang\\Xshell 7\\;D:\\Program Files (x86)\\NetSarang\\Xftp 7\\;D:\\Program Files\\MongoDB\\mongosh\\bin;D:\\Program Files\\MongoDB\\Server\\6.0\\bin;D:\\Program Files\\Java\\jdk8\\bin;D:\\Program Files\\Java\\jdk8\\lib;D:\\Program Files\\Git\\cmd;D:\\Program Files\\DevEnv\\apache-maven-3.8.4\\bin;%TOMCAT_HOME\\bin;D:\\Program Files\\nodejs\\;D:\\Program Files\\DevEnv\\mysql\\bin;D:\\Program Files\\Go\\bin;C:\\Users\\Gavin\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Gavin\\AppData\\Roaming\\npm;C:\\Users\\Gavin\\go\\bin;D:\\Microsoft VS Code\\bin")
for i, s := range list {
fmt.Println("SplitList:----->", i, "--", s)
}
for i, s := range list1 {
fmt.Println("SplitList1:----->", i, "--", s)
}
println("替换分割符号的结果:", filepath.ToSlash(fpath))
if err != nil {
fmt.Println("err")
return
}
}
运行结果:
文件绝对路径--D:\main.go
文件所在路径--D:\
路径最后一个元素--main.go
该文件所在的目录:D:/ 文件名:main.go
该文件所在的目录:D:\Program Data\IdeaProject\smart-admin\ 文件名:README.md
文件是否符合正则要求:true
文件路径true D 开头
求值后的路径名1D:\main.go
求值后的路径名2
CreateFile C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Adobe Photoshop 2023.ink: The system cannot find the file specified.
Glob---->> 0 --- .idea
Glob---->> 1 --- go.mod
Glob---->> 2 --- main.go
Glob---->> 3 --- stringCut.go
Glob1---->> 0 --- D:\ChwenyuLim\jdk-11doc\docs\specs\rmi
相对路径: main.go
SplitList:-----> 0 -- D:/main.go
SplitList1:-----> 0 -- D:\Program Files (x86)\VMware\VMware Workstation\bin\
SplitList1:-----> 1 -- C:\Windows\system32
SplitList1:-----> 2 -- C:\Windows
SplitList1:-----> 3 -- C:\Windows\System32\Wbem
SplitList1:-----> 4 -- C:\Windows\System32\WindowsPowerShell\v1.0\
SplitList1:-----> 5 -- C:\Windows\System32\OpenSSH\
SplitList1:-----> 6 -- C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common
SplitList1:-----> 7 -- C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR
SplitList1:-----> 8 -- C:\WINDOWS\system32
SplitList1:-----> 9 -- C:\WINDOWS
SplitList1:-----> 10 -- C:\WINDOWS\System32\Wbem
SplitList1:-----> 11 -- C:\WINDOWS\System32\WindowsPowerShell\v1.0\
SplitList1:-----> 12 -- C:\WINDOWS\System32\OpenSSH\
SplitList1:-----> 13 -- D:\Program Files (x86)\NetSarang\Xshell 7\
SplitList1:-----> 14 -- D:\Program Files (x86)\NetSarang\Xftp 7\
SplitList1:-----> 15 -- D:\Program Files\MongoDB\mongosh\bin
SplitList1:-----> 16 -- D:\Program Files\MongoDB\Server\6.0\bin
SplitList1:-----> 17 -- D:\Program Files\Java\jdk8\bin
SplitList1:-----> 18 -- D:\Program Files\Java\jdk8\lib
SplitList1:-----> 19 -- D:\Program Files\Git\cmd
SplitList1:-----> 20 -- D:\Program Files\DevEnv\apache-maven-3.8.4\bin
SplitList1:-----> 21 -- %TOMCAT_HOME\bin
SplitList1:-----> 22 -- D:\Program Files\nodejs\
SplitList1:-----> 23 -- D:\Program Files\DevEnv\mysql\bin
SplitList1:-----> 24 -- D:\Program Files\Go\bin
SplitList1:-----> 25 -- C:\Users\Gavin\AppData\Local\Microsoft\WindowsApps
SplitList1:-----> 26 -- C:\Users\Gavin\AppData\Roaming\npm
SplitList1:-----> 27 -- C:\Users\Gavin\go\bin
SplitList1:-----> 28 -- D:\Microsoft VS Code\bin
数据的类型--*os.fileStat
main.go
57
false
-rw-rw-rw-
-----------分隔符--------------
如果是目录名 \
最短路径 D:\main.go
最短路径 D:\Program Data\IdeaProject\smart-admin\README.md
D:\main.go
文件的扩展名: .go
替换\后的结果 D:\main.go
是本地文件吗? false
分割符号1 ;
分割符号2 \
替换分割符号的结果: D:/main.go
2023-05-21 22:15:21.0636266 +0800 CST
Process finished with the exit code 0
splitlist参数来源:本机系统环境变量
文件常规操作
创建文件/打开关闭文件
func FileOpera(){
//创建目录时,如果目录存在,则创建失败
err := os.Mkdir("mkdir", os.ModeDir) //创建目录
if err!=nil {
fmt.Println("创建失败")
}
//如果目录存在在则返回nil
err = os.MkdirAll("mkdir"+string(filepath.Separator)+"test", os.ModePerm)
if err!=nil {
fmt.Println("创建失败")
}
//创建临时文件
temp, err := os.MkdirTemp("./", "*")
fmt.Println(temp)
//创建文件 os.Create()创建文件,如果文件存在,会将其覆盖
create, err := os.Create("hi.go")//返回FileInfo
println("文件名:",create.Name())
stat, err := create.Stat()
fmt.Println("创建时间",stat.ModTime())
createTemp, err := os.CreateTemp(".\\", "^A")
println(createTemp.Name())
}
func main() {
FileOpera()
}
创建失败
./249639028
文件名: hi.go
创建时间 2023-05-24 09:30:04.5023955 +0800 CST
.\^A2747869728
删除文件
func main() {
//FileFunc()
delFile()
}
func delFile(){
err2 := os.Remove("D:/test.java") //删除文件
//如果文件不存在,则不反返回
if err2==nil{
fmt.Println("删除成功")
}
err := os.Remove("D:\\zookeeper") //删除非空目录时会失败
if err!=nil{
fmt.Println(err.Error())//remove D:\zookeeper: The directory is not empty.
}
os.RemoveAll("回收站")
}
文件读与写
在go中读取/写入的流程—>>
- os 包下打开文件
os.open(文件名)—>按照默认策略读/写
os.openfile(参数可设定文件内的读取/写入策略)
- 获得返回值之后可以通过Read/Write 方法读取/写入文件
- 最后关闭文件
代码Demo
//读取文件流程
func RFile() {
//打开文件
open, err := os.Open("D:/main.txt")
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(open.Name(), "打开成功")
bytes := make([]byte, 256)
//open.Read(b[]byte) 从文件中最多读取 len(b)个字节并存储在b中
// Read reads up to len(b) bytes from the File and stores them in b.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF. //最后返回 0 , io.EOF
for { //循环读取
read, err := open.Read(bytes) //一次读取的byte 最后不足 len(bytes)则以空格补齐
if read == 0 || err == io.EOF {
fmt.Println("读取结束")
break
}
fmt.Println(string(bytes))
}
}
open.Close()
}
func WFile() {
open, err := os.OpenFile("D:/main.txt", 1, os.ModePerm)
if err != nil {
fmt.Println("打开文件失败")
return
}
fmt.Println(open.Name(), "打开成功")
bytes := make([]byte, 64) //读
file, err := os.Open("D:/main.go")
for {
read, err := file.Read(bytes)//读
open.Write(bytes)//写如
if read == 0 || err == io.EOF {
fmt.Println("写入结束")
break
}
}
writeString, err := open.WriteString("全世界都在卷")
if err != nil {
fmt.Println("写入失败")
} else {
fmt.Println("写入成功", writeString)
}
defer open.Close()
}
func main() {
RFile()
WFile()
}
写入的内容
文件的拷贝
func copyFileD(src, des string) (int64, error) {
srcfile, err := os.OpenFile(src, 1, fs.ModePerm)
if err != nil {
fmt.Println(srcfile.Name(), "src打开失败")
return -1,err
}
fmt.Println(srcfile.Name(), "src打开成功")
desfile, err := os.OpenFile(des, 1, fs.ModePerm)
if err != nil {
fmt.Println(desfile.Name(), "des打开失败")
return -1 ,err
}
fmt.Println(desfile.Name(), "des打开成功")
defer srcfile.Close()
defer desfile.Close()
return io.Copy(desfile,srcfile )
}
func main() {
src:=".\\main.txt"
des:=".\\copymain.txt"
d, err := copyFileD(src, des)
if err!=nil {
fmt.Println("拷贝失败",err.Error())
}else{
fmt.Println("拷贝成功",d)
}
}
1,当被拷贝的文件的不是只读的(这里只flag为 1)那么就会拷贝失败—access denied
拷贝失败 read .\main.txt: Access is denied.
2,如果拷贝文件的目标文件不存在,会panic
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0xf95308]
ioutil包下API
func ioutiFunc() {
path := "./main.txt"
//读取所有数据返回字节数组 该方法已被废弃,使用os.ReadFile(path)
file, err := ioutil.ReadFile(path) //底层是os.ReadFile(filename)
readFile, err := os.ReadFile(path)
if err!=nil {
fmt.Println(err.Error())
}else{
fmt.Println(string(file))
fmt.Println(string(readFile))
}
bytes := make([]byte, 32)
for i := 0; i < len(bytes); i++ {
bytes[i]='1'
}
//一开始不存在main2.txt
err = ioutil.WriteFile("./main2.txt", bytes, fs.ModePerm)
if err!=nil{
println("写入失败")
}else{
println("写入成功")
}
//废弃
/* 使用下面的方式
f, err := os.Open(dirname)
if err != nil {
return nil, err
}
list, err := f.Readdir(-1)
f.Close()
*/
//读取一个目录下的子内容(子文件夹和子目录) 仅取第一层不会级联
dir, err := ioutil.ReadDir("D:\\hadoop-3.3.4\\libexec\\tools")
for _, info := range dir {
fmt.Println(info)
}
//在当前目录下.创建一个以指定字符串为名称前缀的临时文件,并返回文件夹路径
tempDir, err := ioutil.TempDir("./", "^abcd")//底层调用os.MkdirTemp(dir, pattern)
fmt.Println(tempDir)
//在当前目录下,创建一个以指定字符串为名称前缀的文件,并以读写模式打开,返回os.file指针对象
tempFile, err := ioutil.TempFile("./", "*")
fmt.Println(tempFile)
}
func main() {
ioutiFunc()
}
该包下的一些API逐渐会被废弃,在go1.20版本下都被废弃了
bufio包下API
bufio实现了带缓冲的Io操作,达到搞笑的读写,就像java中的buffer;
bufio包对io包下的对象Reader、Writer进行包装,分别实现了io.Reader和io.Writer接口,提供了数据缓冲功能,能够一定程度减少大块数据读写带来的开销,所以bufio比直接读写更快。
bufio的常用API
NewReader
func NewReader(rd io.Reader) *Reader
将reader封装成一个大小为size的缓存对象,
底层核心是NewReaderSize(rd, defaultBufSize)
代码demo
func bufIoT(){
pathF:="./main2.txt"
//参数rd io.Reader 返回值*Reader
//返回一个默认 defaultBufSize = 4096 大小的缓冲区
open, _ := os.Open(pathF)
reader := bufio.NewReader(io.LimitReader(open, 1024)) //底层NewReaderSize(rd, defaultBufSize)
fmt.Printf("%p \n",reader)
for {
readString, err := reader.ReadString('\n')//按分割符进行读取
if err==io.EOF{
fmt.Println("读取完毕",readString)
break
}
}
open.Close()
}
func main() {
bufIoT()
}
readString(str string)
func (b *Reader) ReadString(delim byte) (string, error)
按分割符进行读取,即在文件中查找 指定的分隔符,读取该分隔符之前的所有数据;
如果没有找到,则返回全部数据;
如果在找到delim之前遇到错误,则返回遇到错误之前的所有数据,同时返回遇到的错误(通常是io.EOF)。只有当找不到delim时,err才不为nil。
底层核心是 full, frag, n, err := b.collectFragments(delim)
代码Demo
func main() {
bufIoStr()
bufioP()
}
func bufioP() {
file1, err := os.Open("./main2.txt") //file1 类型 * File File结构体
if err != nil {
fmt.Println("打开失败")
} else {
fmt.Println("打开成功")
}
//LimitReader 返回一个从 r 读取的 Reader
//但在 n 个字节后以 EOF 停止。
newRe := bufio.NewReaderSize(bufio.NewReader(file1), 128) //Reader是一个接口 File 可以直接给 Reader赋值,说明File实现了Reader
bytes := make([]byte, 1024)
for {
/*
读取将数据读入 p。
它返回读入 p 的字节数。
字节取自底层读取器上最多一个读取,
因此 n 可能小于 len(p)。
要准确读取 len(p) 字节,请使用 io。ReadFull(b, p).
如果基础读取器可以使用 io 返回非零计数。EOF,
*/
read, err := newRe.Read(bytes)//此 read读取的数据返回的字节数肯能不准
if err != io.EOF {
fmt.Println(read)
}else{
fmt.Println(string(bytes))
fmt.Println("读取结束")
break
}
}
file1.Close()
}
对于简单的任务,使用Scanner可能更方便。
ReadLine()
ReadLine()通过调用ReadSlice()方法实现,返回的也是缓存的切片。ReadLine()尝试返回一个单行数据,不包括行尾标记(\n或\r\n),如果在缓存中找不到行尾标记,则设置isPrefix为true,表示查找未完成,同时读出缓存中的数据并作为切片返回;只有在当前缓存中找到行尾标记,才将isPrefix设置为false,表示查找完成。可以多次调用ReadLine()来读出一行,返回的数据在下一次读取操作之前是有效的。如果ReadLine()无法获取任何数据,则返回一个错误信息(通常是io.EOF)。
代码Demo
func ScannP() {
var (
i int
b bool
s string
f float32
)
newReader := strings.NewReader("46 true 3.4 Hello World") //
file1, err := os.Open("./main2.txt")
fscan, err := fmt.Fscan(newReader,&i,&b,&f,&s)
if err != nil {
fmt.Println(err.Error())
}
fmt.Printf("文本中的读取到的数据数%d\n i值%d \n s值%s \nb值%t \nf值%f \n",fscan,i,s,b,f)
reader := bufio.NewReader(file1)
for {
line, _, err := reader.ReadLine()//line, err = b.ReadSlice('\n')
if err==io.EOF {
break
}
fmt.Println(string(line))
}
}