golang中的文件操作
一、文件的基本介绍
文件是数据源(保存数据的地方)的一种,比如经常使用的word文档,txt文件,excel文件都是文件。文件最主要的作用就是保存数据,它既可以保存一张图片,也可以保持视频,声音等等。
1、输入流和输出流
2、os.File封装了所以文件相关操作,File是一个结构体
type File struct {
// 内含隐藏或非导出字段
}
File代表一个打开的文件对象。
func (f *File) Read(b []byte) (n int, err error)
Read方法从f中读取最多len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取0个字节且返回值err为io.EOF。
func (f *File) ReadAt(b []byte, off int64) (n int, err error)
ReadAt从指定的位置(相对于文件开始位置)读取len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。当n<len(b)时,本方法总是会返回错误;如果是因为到达文件结尾,返回值err会是io.EOF。
func (f *File) Write(b []byte) (n int, err error)
Write向文件中写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。
func (f *File) WriteAt(b []byte, off int64) (n int, err error)
WriteAt在指定的位置(相对于文件开始位置)写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。
二、打开文件和关闭文件
func Open(name string) (file *File, err error)
Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。
func (f *File) Close() error
Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package main import ( "fmt" "os" ) func main() { //打开文件 //file叫做对象、指针或者文件句柄 file, err := os.Open( "/Users/test/Desktop/demo/ssh.txt" ) if err != nil { fmt.Println( "open fiel err=" , err) } fmt.Printf( "file=%v,%T" , *file, file) err = file.Close() if err != nil { fmt.Println( "close file err=" , err) } } |
三、读写文件操作
读取文件的内容并显示在终端(带缓冲区的方式),使用os.Open,file.Close,bufio.NewReader(),reader.ReadString函数和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package main import ( "bufio" "fmt" "io" "os" ) func main() { //打开文件 //file叫做对象、指针或者文件句柄 file, err := os.Open( "/Users/test/Desktop/demo/ssh.txt" ) if err != nil { fmt.Println( "open fiel err=" , err) } //当函数退出时,要及时关闭file,否则会有内存泄漏 defer file.Close() if err != nil { fmt.Println( "close file err=" , err) } // 创建一个 *Reader ,是带缓冲的 /* const ( defaultBufSize = 4096 //默认的缓冲区为 4096 ) */ reader := bufio.NewReader(file) //循环读取文件内容 for { str, err := reader.ReadString( '\n' ) //读到一个换行就结束 if err == io.EOF { //io.EOF表示文件的末尾 break } //输出内容 fmt.Print(str) } fmt.Println( "文件读取结束..." ) } |
读取文件的内容并显示在终端(使用ioutil一次将整个文件读入到内存中),这种方式适用于文件不大的情况。相关方法和函数(ioutil.ReadFile)
func ReadFile(filename string) ([]byte, error)
ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package main import ( "fmt" "io/ioutil" ) func main() { //使用ioutil.ReadFile一次性将文件读取到位 file := "/Users/test/Desktop/bytedance/ssh.txt" content, err := ioutil.ReadFile(file) if err != nil { fmt.Printf( "read file err=%v" , err) } //把读取到的内容显示到终端 fmt.Printf( "%v\n" , content) //[]byte fmt.Printf( "%v" , string(content)) //没有显式的Open文件,也不需要显式Close文件 //文件的Open和Close被封装到ReadFile函数内部 } |
os.OpenFile 函数
func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
OpenFile是一个更一般性的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果操作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。
第二个参数:文件打开模式(可以组合)
const (
O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
O_RDWR int = syscall.O_RDWR // 读写模式打开文件
O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件
O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在
O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O
O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件
)
第三个参数:权限控制
r -> 4
w -> 2
x -> 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package main import ( "bufio" "fmt" "os" ) func main() { filePath := "/Users/test/Desktop/test.txt" file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { fmt.Printf( "open file err=%v\n" , err) return } defer file.Close() str := "hello,world\n" //写入时使用带缓存的*Writer writer := bufio.NewWriter(file) for i := 0; i < len(str); i++ { writer.WriteString(str) } //writer是带缓存的,在调用WriterString方法时,内容是先写入缓存的, //需要调用Flush方法,将缓冲的数据真正写入到文件中,否则文件中会没有数据 writer.Flush() } |
打开一个存在的文件,将原来的内容覆盖成新的10句话“欢迎来到go的世界”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package main import ( "bufio" "fmt" "os" ) func main() { filePath := "/Users/test/Desktop/test.txt" file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0666) if err != nil { fmt.Printf( "open file err=%v\n" , err) return } defer file.Close() str := "欢迎来到go的世界\n" //写入时使用带缓存的*Writer writer := bufio.NewWriter(file) for i := 0; i < 10; i++ { writer.WriteString(str) } //writer是带缓存的,在调用WriterString方法时,内容是先写入缓存的, //需要调用Flush方法,将缓冲的数据真正写入到文件中,否则文件中会没有数据 writer.Flush() } |
打开一个存在的文件,在原来的内容后面追加内容“欢天喜地过大年”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package main import ( "bufio" "fmt" "os" ) func main() { filePath := "/Users/test/Desktop/test.txt" file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0666) if err != nil { fmt.Printf( "open file err=%v\n" , err) return } defer file.Close() str := "欢天喜地过大年!\r\n" //写入时使用带缓存的*Writer writer := bufio.NewWriter(file) for i := 0; i < 10; i++ { writer.WriteString(str) } //writer是带缓存的,在调用WriterString方法时,内容是先写入缓存的, //需要调用Flush方法,将缓冲的数据真正写入到文件中,否则文件中会没有数据 writer.Flush() } |
打开一个存在的文件,将原来的内容读出显示在终端,并且追加 5 句"hello,北京!"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package main import ( "bufio" "fmt" "io" "os" ) func main() { filePath := "/Users/test/Desktop/test.txt" file, err := os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0666) if err != nil { fmt.Printf( "open file err=%v\n" , err) return } defer file.Close() //写入时使用带缓存的*Writer reader := bufio.NewReader(file) for { str, err := reader.ReadString( '\n' ) if err == io.EOF { break } fmt.Print(str) } str := "hello,北京!\r\n" writer := bufio.NewWriter(file) for i := 0; i < 10; i++ { writer.WriteString(str) } //writer是带缓存的,在调用WriterString方法时,内容是先写入缓存的, //需要调用Flush方法,将缓冲的数据真正写入到文件中,否则文件中会没有数据 writer.Flush() } |
编程一个程序,将一个文件的内容,写入到另外一个文件。注:这两个文件已经存在了。使用ioutil.ReadFile/ioutil.WriteFile完成写文件的任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package main import ( "fmt" "io/ioutil" ) func main() { filePath1 := "/Users/test/Desktop/test.txt" filePath2 := "/Users/test/Desktop/dest.txt" data, err := ioutil.ReadFile(filePath1) if err != nil { fmt.Printf( "read file err=%v\n" , err) return } err = ioutil.WriteFile(filePath2, data, 0666) if err != nil { fmt.Printf( "write file error=%v\n" , err) } } |
判断文件是否存在
golang判断文件或文件夹是否存在的方法为使用os.Stat()函数返回的错误值进行判断:
(1)、如果返回的错误为nil,说明文件或文件夹存在
(2)、如果返回的错误类型使用os.IsNotExist()判断为true,说明文件或文件夹不存在
(3)、如果返回的错误为其它类型,则不确定是否存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package main import ( "fmt" "os" ) func PathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err } func main() { filePath := "/Users/test/Desktop/test.txt" fmt.Println(PathExists(filePath)) } |
拷贝文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package main import ( "bufio" "fmt" "io" "os" ) func CopyFile(dstFileName string, srcFileName string) (written int64, err error) { srcFile, err := os.Open(srcFileName) if err != nil { fmt.Printf( "open file err=%v\n" , err) } defer srcFile.Close() reader := bufio.NewReader(srcFile) dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { fmt.Printf( "open file err=%v\n" , err) return } writer := bufio.NewWriter(dstFile) defer dstFile.Close() return io.Copy(writer, reader) } func main() { srcFile := "/Users/bytedance/Desktop/cao.png" dstFile := "/Users/bytedance/Desktop/dst.png" _, err := CopyFile(dstFile, srcFile) if err == nil { fmt.Printf( "文件拷贝完成\n" ) } else { fmt.Printf( "文件拷贝出错,err=%v" , err) } } |
统计英文、数字、空格和其他字符数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package main import ( "bufio" "fmt" "io" "os" ) type CharCount struct { ChCount int //记录英文个数 NumCount int //记录数字个数 SpaceCount int //记录空格个数 OtherCount int //记录其它字符的个数 } func main() { fileName := "/Users/bytedance/Desktop/test.txt" file, err := os.Open(fileName) if err != nil { fmt.Printf( "open file err=%v\n" , err) return } defer file.Close() var count CharCount reader := bufio.NewReader(file) for { s, err := reader.ReadString( '\n' ) if err == io.EOF { break } //为了兼容中文字符,将str转成[]rune str := []rune(s) for _, v := range str { switch { case v >= 'a' && v <= 'z' : fallthrough //穿透 case v >= 'A' && v <= 'Z' : count.ChCount++ case v == ' ' || v == '\t' : count.SpaceCount++ case v >= '0' && v <= '9' : count.NumCount++ default : count.OtherCount++ } } } fmt.Printf( "字符的个数=%v 数字的个数=%v 空格的个数=%v 其它字符个数=%v" , count.ChCount, count.NumCount, count.SpaceCount, count.OtherCount) } |
四、命令行参数
os.Args是一个string的切片,用来存储所有的命令行参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package main import ( "fmt" "os" ) func main() { fmt.Println( "命令行参数有" , len(os.Args)) //遍历os.Args切片可以得到所有的命令行输入参数值 for i, v := range os.Args { fmt.Printf( "args[%v]=%v\n" , i, v) } } |
flag包解析命令行参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package main import ( "flag" "fmt" ) func main() { //定义变量用于接收命令行参数值 var user string var pwd string var host string var port int flag.StringVar(&user, "u" , "" , "用户名,默认为空" ) flag.StringVar(&pwd, "pwd" , "" , "密码,默认为空" ) flag.StringVar(&host, "h" , "localhost" , "主机名,默认为localhost" ) flag.IntVar(&port, "port" , 3306, "端口号,默认为3306" ) //转换操作,必须调用该方法 flag.Parse() fmt.Printf( "user=%v pwd=%v host=%v port=%v\n" , user, pwd, host, port) } |
五、json
轻量级的数据交换格式。json易于机器解析和生成,并有效地提升网络传输效率,通常程序在网络传输时先将数据(结构体、map等)序列化成json字符串,到接收方得到json字符串时,在反序列化恢复成原来的数据类型(结构体、map等)。这种方式已然成为各个语言的标准。
1、json的序列化
json序列化是指将有key-value结构的数据类型(比如结构体、map、切片)序列化成json字符串的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | package main import ( "encoding/json" "fmt" ) type Monster struct { Name string Age int Birthday string Sal float64 Skill string } //序列化结构体 func testStruct() { monster := Monster{ Name: "牛魔王" , Age: 500, Birthday: "2011-11-11" , Sal: 8000.0, Skill: "牛魔拳" , } //将monster序列化 data, err := json.Marshal(&monster) if err != nil { fmt.Printf( "序列化错误 err=%v\n" , err) } //输出序列化后的结果 fmt.Printf( "monster序列化后=%v\n" , string(data)) } //map序列化 func testMap() { var a map [string] interface {} a = make( map [string] interface {}) a[ "name" ] = "红孩儿" a[ "age" ] = 30 a[ "address" ] = "洪崖洞" data, err := json.Marshal(a) if err != nil { fmt.Printf( "序列化错误 err=%v\n" , err) } //输出序列化后的结果 fmt.Printf( "map序列化后=%v\n" , string(data)) } //切片序列化 func testSlice() { var slice [] map [string] interface {} var m1 map [string] interface {} m1 = make( map [string] interface {}) m1[ "name" ] = "jack" m1[ "age" ] = "7" m1[ "addresss" ] = "北京" slice = append(slice, m1) var m2 map [string] interface {} m2 = make( map [string] interface {}) m2[ "name" ] = "tom" m2[ "age" ] = "20" m2[ "address" ] = [2]string{ "墨西哥" , "夏威夷" } slice = append(slice, m2) data, err := json.Marshal(slice) if err != nil { fmt.Printf( "序列化错误 err=%v\n" , err) } //输出序列化后的结果 fmt.Printf( "slice 序列化后=%v\n" , string(data)) } //对基本数据类型序列化,对基本数据类型进行序列化意义不大 func testFloat64() { var num float64 = 2345.67 data, err := json.Marshal(num) if err != nil { fmt.Printf( "序列化错误 err=%v\n" , err) } //输出序列化后的结果 fmt.Printf( "num序列化后=%v\n" , string(data)) } func main() { testStruct() testMap() testSlice() testFloat64() } |
对于结构体的序列化,如果希望序列化后的key的名字重新制定,那么可以给struct指定一个tag标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | package main import ( "encoding/json" "fmt" ) type Monster struct { Name string `json: "monster_name" ` Age int `json: "monster_age" ` Birthday string Sal float64 Skill string } //序列化结构体 func testStruct() { monster := Monster{ Name: "牛魔王" , Age: 500, Birthday: "2011-11-11" , Sal: 8000.0, Skill: "牛魔拳" , } //将monster序列化 data, err := json.Marshal(&monster) if err != nil { fmt.Printf( "序列化错误 err=%v\n" , err) } //输出序列化后的结果 fmt.Printf( "monster序列化后=%v\n" , string(data)) } func main() { testStruct() } |
2、json的反序列化
json反序列化是指将json字符串反序列化成对应的数据类型(比如结构体、map、切片)的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | package main import ( "encoding/json" "fmt" ) type Monster struct { Name string Age int Birthday string Sal float64 Skill string } //反序列化结构体 func unmarshalStruct() { str := "{\"Name\":\"牛魔王\",\"Age\":500,\"Birthday\":\"2011-11-11\",\"Sal\":8000,\"Skill\":\"牛魔拳\"}" var monster Monster err := json.Unmarshal([]byte(str), &monster) if err != nil { fmt.Printf( "unmarshal err=%v\n" , err) } fmt.Printf( "反序列化后 monster=%v monster.Name=%v\n" , monster, monster.Name) } //map序列化 func testMap() string { var a map [string] interface {} a = make( map [string] interface {}) a[ "name" ] = "红孩儿" a[ "age" ] = 30 a[ "address" ] = "洪崖洞" data, err := json.Marshal(a) if err != nil { fmt.Printf( "序列化错误 err=%v\n" , err) } //输出序列化后的结果 fmt.Printf( "map序列化后=%v\n" , string(data)) return string(data) } //反序列化map func unmarshalMap() { str := "{\"address\":\"洪崖洞\",\"age\":30,\"name\":\"红孩儿\"}" var a map [string] interface {} //反序列化map不需要make,因为make操作被封装到Unmarshal函数 err := json.Unmarshal([]byte(str), &a) if err != nil { fmt.Printf( "unmarshal err=%v\n" , err) } fmt.Printf( "反序列化后 a=%v\n" , a) } //反序列化map func unmarshalMap2() { str := testMap() var a map [string] interface {} //反序列化map不需要make,因为make操作被封装到Unmarshal函数 err := json.Unmarshal([]byte(str), &a) if err != nil { fmt.Printf( "unmarshal err=%v\n" , err) } fmt.Printf( "反序列化后 a=%v\n" , a) } //反序列化切片 func unmarshalSlice() { str := "[{\"address\":\"北京\",\"age\":\"7\",\"name\":\"jack\"}," + "{\"address\":[\"墨西哥\",\"夏威夷\"],\"age\":\"20\",\"name\":\"tom\"}]" var slice [] map [string] interface {} err := json.Unmarshal([]byte(str), &slice) if err != nil { fmt.Printf( "unmarshal err=%v\n" , err) } fmt.Printf( "反序列化后 slice=%v\n" , slice) } func main() { unmarshalStruct() unmarshalMap() unmarshalMap2() unmarshalSlice() } |
在反序列化一个json字符串时,要确保反序列化后的数据类型和原来序列化前的数据类型一致。
如果json字符串是通过程序获取到的,则不需要再对“转义处理。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· 程序员常用高效实用工具推荐,办公效率提升利器!
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 【译】WinForms:分析一下(我用 Visual Basic 写的)
2018-04-07 187 Repeated DNA Sequences 重复的DNA序列
2018-04-07 179 Largest Number 把数组排成最大的数
2018-04-07 174 Dungeon Game 地下城游戏
2018-04-07 173 Binary Search Tree Iterator 二叉搜索树迭代器
2018-04-07 172 Factorial Trailing Zeroes 阶乘后的零
2018-04-07 171 Excel Sheet Column Number Excel表列序号 26进制转10进制
2018-04-07 169 Majority Element 求众数 数组中出现次数超过一半的数字