文件对象 对文件的操作对象是os.File
1 2 3 4 5 6 7 8 9 10 11 12 type File struct { *file } type file struct { pfd poll.FD name string dirinfo *dirInfo nonblock bool stdoutOrErr bool appendMode bool }
对文件的操作 打开文件和关闭文件
1 2 f, _ := os.Open("1.txt" ) f.Close()
File
拥有的方法实现了io
包中的很多接口,例如以下几个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type Reader interface { Read(p []byte ) (n int , err error ) } type Writer interface { Write(p []byte ) (n int , err error ) } type Closer interface { Close() error } type Seeker interface { Seek(offset int64 , whence int ) (int64 , error ) }
缓冲对象 对数据I/O
接口的缓冲功能,在bufio
包中,分为读缓冲和写缓冲。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type Reader struct { buf []byte rd io.Reader r, w int err error lastByte int lastRuneSize int } type Writer struct { err error buf []byte n int wr io.Writer }
对文件操作 对File
的操作可以直接读,也可以通过缓冲来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 f, _ := os.Open("1.txt" ) defer f.Close()b := make ([]byte , 1 <<20 ) n, err := f.Read(b) if err != nil && err != io.EOF { log.Fatal(err) } fmt.Println(n, string (b))
或者通过ioutil
包读全部取出来
1 2 f, _ := os.Open("1.txt" ) b, err := ioutil.ReadAll(f)
或者
1 b, err := ioutil.ReadFile("1.txt" )
从某个位置开始读,设置偏移量
1 (f *File) ReadAt(b []byte, off int64) (n int, err error) // 读取长度为b的数据
创建文件
1 2 3 os.Create("3.txt" ) _, err := os.OpenFile("2.txt" , os.O_APPEND|os.O_CREATE, 0444 )
写入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 f, err := os.OpenFile("2.txt" , os.O_APPEND|os.O_RDWR, 0755 ) if err != nil { log.Fatal(err) } defer f.Close()writer := bufio.NewWriter(f) _, err = writer.WriteString("abc" ) if err != nil { log.Fatal(err) } err = writer.Flush() if err != nil { log.Fatal(err) }
读写模式
1 2 3 4 5 6 7 8 9 10 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_SYNC int = syscall.O_SYNC O_TRUNC int = syscall.O_TRUNC
拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 f1 := "1.txt" f2 := "2.txt" data, err := ioutil.ReadFile(f1) if err != nil { log.Fatal(err) } file, err := os.OpenFile(f2, os.O_CREATE|os.O_WRONLY, 0766 ) if err != nil { log.Fatal(err) } defer file.Close()_, err = file.Write(data) if err != nil { log.Fatal(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 f1 := "1.txt" f2 := "2.txt" file1, err := os.Open(f1) if err != nil { log.Fatal(err) } defer file1.Close()reader := bufio.NewReader(file1) file2, err := os.OpenFile(f2, os.O_CREATE|os.O_WRONLY, 0766 ) if err != nil { log.Fatal(err) } defer file2.Close()writer := bufio.NewWriter(file2) _, err = io.Copy(writer, reader) if err != nil { log.Fatal(err) }
或者使用io.util
包
1 2 3 4 5 bytes, err := ioutil.ReadFile(f1) if err != nil { log.Fatal(err) } ioutil.WriteFile(f2, bytes, 0755 )
判断数字、英文、符号、汉字
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 f, _ := os.Open("1.txt" ) defer f.Close()reader := bufio.NewReader(f) var han int var english int var num int var other int for { str, err := reader.ReadString('\n' ) if err == io.EOF { break } for _, s := range str { switch { case unicode.Is(unicode.Han, s): han++ case unicode.IsLetter(s): english++ case unicode.IsNumber(s): num++ default : other++ } } } fmt.Printf("han %d,english %d,num %d,other %d\n" , han, english, num, other)
通过unicode
包可以判断字符属性。
判断文件状态等属性
1 2 3 4 5 stat, err := os.Stat("100.txt" ) if os.IsNotExist(err) { log.Fatal("file not exist" ) } fmt.Println(stat.Name(), stat.IsDir(), stat.Mode(), stat.Size(), stat.ModTime())
如果文件存在,打印文件名称,是否是目录,文件权限,文件大小,修改时间
1 1.txt false -rw-r--r-- 1171 2022-08-01 17:54:06.072619357 +0800 CST
删除文件
1 os.Remove("1.txt") // 如果文件不存在,则会报错
对目录操作 创建目录
重命名
列举文件夹中的文件
1 2 3 4 5 6 7 8 9 files, _ := ioutil.ReadDir("file" ) for _, file := range files { if file.IsDir() { fmt.Print("目录" ) } else { fmt.Print("文件" ) } fmt.Println(file.Name()) }
便利目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func getFiles (path string , deep string ) { files, _ := ioutil.ReadDir(path) emp := "" for i := 0 ; i < len (deep); i++ { emp += " " } deep += "-" for _, file := range files { if file.IsDir() { fmt.Println(emp + "|" + deep + string (os.PathSeparator) + file.Name()) getFiles(path+string (os.PathSeparator)+file.Name(), deep) } else { fmt.Println(emp + "|" + deep + file.Name()) } } }
删除目录
获取程序运行的目录
修改目录
创建多层级目录
1 os.MkdirAll("a/b/c" , 0777 )
操作绝对路径
ioutil包 1 2 3 4 5 6 7 ReadDir(dirname string) ([]fs.FileInfo, error) // 便利目录下的所有文件 ReadFile(filename string) ([]byte, error) // 读取文件内容 ReadAll(r io.Reader) ([]byte, error) // 读取Reader的所有内容 WriteFile(filename string, data []byte, perm fs.FileMode) error // 将data写入文件,当文件不存在则创建 TempFile(dir string, pattern string) (f *os.File, err error) // 创建以这个前缀为名的临时文件 TempFile(dir string, pattern string) (f *os.File, err error) // 创建以这个前缀为名的临时目录 NopCloser(r io.Reader) io.ReadCloser // 将r包装为一个ReadCloser类型
读取内存
1 2 3 4 s := "abc" reader := strings.NewReader(s) bytes, _ := ioutil.ReadAll(reader) fmt.Println(string (bytes))
I/O操作 输出到Stdout
1 2 fmt.Fprintln(os.Stdout, "abc" ) os.Stdout.WriteString("abc" )
或者使用缓存
1 2 3 4 5 6 7 buffer := bytes.NewBufferString("abc" ) fmt.Fprintln(buffer, "def" ) fmt.Println(buffer.String()) writer := bufio.NewWriter(os.Stdout) writer.WriteString("anc" ) writer.Flush()
读取 从输入中读取
1 2 3 4 var num int fmt.Fscan(os.Stdin, &num) fmt.Scanln(&num) fmt.Println(num)
这里有个共同的特点,都是操作io.Writer
和io.Reader
读取文件内容到内存
1 2 3 4 5 6 7 8 9 10 11 12 f, err := os.Open("2.txt" ) if err != nil { log.Fatal(err) } defer f.Close()buf := make ([]byte , 10 ) n, err := f.Read(buf) if err != nil { log.Fatal(err) } fmt.Println(n, string (buf))
ReadByte
:读一个字节
1 2 3 4 5 6 reader := strings.NewReader("abc" ) readByte, err := reader.ReadByte() if err != nil { log.Fatal(err) } fmt.Println(string (readByte))
从文件的某个位置开始读,修改文件指针。在断点续传的时候就可以通过Seek实现。
1 2 3 4 5 6 7 func (f *File) Seek(offset int64, whence int) (ret int64, err error) // 例如 file.Seek(2,io.SeekStart) // 从开始偏移2个字节 // SeekStart = 0 // seek relative to the origin of the file // SeekCurrent = 1 // seek relative to the current offset // SeekEnd = 2 // seek relative to the end
断点续传原理就是通过一个临时文件,记录偏移量,实现记录断点。
例如文件A,文件B和一个临时文件C。
当开始将文件A拷贝到文件B,创建临时文件C;从文件A读取一段内容,写入文件B,写入成功,将写入字节数记录到临时文件C中。断电时,先获取临时文件C的内容,获得已拷贝容量,通过Seek
移动偏移量到读取文件和写入文件的位置,继续读取文件A,写入文件B,写入之后,将写入的总字节数相加,记录到文件C。
缓存操作,ReadBytes
1 2 3 4 5 6 7 8 9 reader := strings.NewReader("abc" ) newReader := bufio.NewReader(reader) readByte, err := newReader.ReadBytes('b' ) if err != nil && err != io.EOF { log.Fatal(err) } fmt.Println(string (readByte)) reader.Reset("abc" ) newReader.Reset(reader)
读一行
1 2 3 4 5 6 7 s := `abc def ghi` reader := strings.NewReader(s) newReader := bufio.NewReader(reader) line, _, _ := newReader.ReadLine() fmt.Println(string(line))
读取一个中文字符,Rune
1 line, _, _ := newReader.ReadRune()
读取分段,通过\n
分割
1 newReader.ReadSlice('\n')
直接读出字符串,通过\n
分割
1 newReader.ReadString('\n')
获取缓冲区的可读字节数
按照某个字节长度读取缓冲区的数据,但是不会从缓冲区将数据拿出来
1 newReader.Peek(2) // 取出2个字节,如果超过缓冲区可读长度或者超过缓冲区总长度,都会报错
写入 创建一个writer
1 2 3 4 5 wr := bytes.NewBuffer(nil) // 创建一个缓冲区,如果nil是[]byte(),则缓冲区已存在该数据 writer := bufio.NewWriter(wr) // 创建缓存写对象 writer.WriteString("abc\n") // 在缓存中写入字符串 writer.Flush() // 将缓存中的数据写入到缓冲区wr fmt.Println(wr.String()) // 获取缓冲区数据
写入
1 2 3 4 Write(p []byte) (nn int, err error) // 写入字节切片 WriteString(s string) (int, error) // 写入字符串 WriteByte(c byte) error // 写入字符型 WriteRune(r rune) (size int, err error) // 写入ASC码型
获取缓存状态
1 2 3 writer.Available() // 获取可缓存字节数 writer.Buffered() // 获取已缓存字节数 writer.Size() // 获取缓存总字节数
将缓存中的数据写入底层io.Writer
,Flush
1 2 3 fmt.Println(writer.Available(), writer.Size()) // 4092 4096 writer.Flush() // 写入io.Writer之后,缓存区为空 fmt.Println(writer.Available(), writer.Size()) // 4096 4096
将数据内容进行稳定存储,刷到硬盘中
1 func (f *File) Sync() error
二进制读写 通过encoding/gob
包实现二进制编码和解码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 file, _ := os.OpenFile("110.txt" , os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644 ) defer file.Close()type User struct { Name string Age int } u := User{ Name: "mitaka" , Age: 18 , } buffer := bytes.NewBuffer(nil ) encoder := gob.NewEncoder(buffer) encoder.Encode(u) buffer.WriteTo(file)
解码
1 2 3 4 5 6 7 8 9 10 11 file, _ := os.OpenFile("110.txt" , os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644 ) defer file.Close()type User struct { Name string Age int } u := User{} reader := bufio.NewReader(file) decoder := gob.NewDecoder(reader) decoder.Decode(&u) fmt.Println(u)
在操作很大的文件时,例如GB
以上文档,需要统计文档中的内容,例如统计行数,可以通过bufio
包的功能实现
统计文件行数
1 2 3 4 5 6 7 8 9 file, _ := os.Open("1.json" ) defer file.Close()scanner := bufio.NewScanner(file) line := 0 for scanner.Scan() { line++ } fmt.Println(line)
压缩和打包 在源码包中的compress
目录中,有gzip
,bzip2
,lzw
等压缩算法和压缩实现方式,这里主要实现zip的使用:
文件压缩和解压缩可以通过源码中的archive/zip
实现
雅座
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 file, _ := os.Create("1.zip" ) defer file.Close() zipW := zip.NewWriter(file) defer zipW.Close() f1, _ := os.Stat("2.txt" ) header, _ := zip.FileInfoHeader(f1) header.Name = f1.Name() + "use zip" header.Method = zip.Deflate f2, _ := os.Open("2.txt" ) defer f2.Close() writer, _ := zipW.CreateHeader(header) io.Copy(writer, f2) f3, _ := os.Open("3.txt" ) defer f3.Close()f3info, _ := f3.Stat() header, _ = zip.FileInfoHeader(f3info) header.Name = f3info.Name() + "f3 zip" header.Method = zip.Deflate writer, _ = zipW.CreateHeader(header) io.Copy(writer, f3)
解压缩
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 reader, _ := zip.OpenReader("1.zip" ) defer reader.Close()outputPath := "out2" _, err := os.Stat(outputPath) if os.IsNotExist(err) { os.MkdirAll(outputPath, 0766 ) } for _, f := range reader.File { fpath := filepath.Join(outputPath, f.Name) if !strings.HasPrefix(fpath, filepath.Clean(outputPath)+string (os.PathSeparator)) { log.Fatal("illegal file path: " , fpath) } if f.FileInfo().IsDir() { os.Mkdir(fpath, f.Mode()) continue } outfile, err := os.OpenFile(fpath, os.O_CREATE|os.O_APPEND|os.O_RDWR, f.Mode()) if err != nil { log.Fatal(err) } defer outfile.Close() rc, err := f.Open() if err != nil { log.Fatal(err) } defer rc.Close() _, err = io.Copy(outfile, rc) if err != nil { log.Fatal(err) } }
通过tar
打包
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 t, err := os.Create("2.tar" ) if err != nil { log.Fatal(err) } defer t.Close()tw := tar.NewWriter(t) defer tw.Close()f1, err := os.Open("1.zip" ) if err != nil { log.Fatal(err) } defer f1.Close()info1, err := f1.Stat() if err != nil { log.Fatal(err) } header1, err := tar.FileInfoHeader(info1, "" ) if err != nil { log.Fatal(err) } err = tw.WriteHeader(header1) if err != nil { log.Fatal(err) } io.Copy(tw, f1)
解打包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 t, err := os.Open("2.tar" ) if err != nil { log.Fatal(err) } defer t.Close()tr := tar.NewReader(t) for hdr, err := tr.Next(); err != io.EOF; hdr, err = tr.Next() { if err != nil { log.Fatal(err) return } fileinfo := hdr.FileInfo() file, err := os.Create(fileinfo.Name()) if err != nil { log.Fatal(err) return } defer file.Close() io.Copy(file, tr) }
各项格式的文件读写 xml
,原生encoding/xml
包支持xml
读写。
写入文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var u = User{ Name: "mitaka" , Age: 18 , Describe: "这是一个人" , } file, err := os.OpenFile("1.xml" , os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755 ) if err != nil { log.Fatal(err) } defer file.Close()encoder := xml.NewEncoder(file) err = encoder.Encode(u) if err != nil { log.Fatal(err) }
写入之后,文件格式化之后:
1 2 3 4 5 <User> <Name>mitaka</Name> <Age>18</Age> <Describe>这是一个人</Describe> </User>
读取xml
文件
1 2 3 4 5 6 7 8 9 10 11 12 file, err := os.Open("1.xml" ) if err != nil { log.Fatal(err) } defer file.Close()var u Userdecoder := xml.NewDecoder(file) err = decoder.Decode(&u) if err != nil { log.Fatal(err) } fmt.Println(u)
json
编码和解码的使用方式,与xml
一致,使用golang自带的序列化包即可。
而xml
、二进制格式的文件解析,则需要引入第三方包。
操作文件内容 读写文件内容的作用之一是可以对内容进行操作
字符串操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 s := "[email protected] " println (strings.Contains(s, "123.com1" )) split := strings.Split(s, "@" ) fmt.Println(split) println (strings.Join(split, "%%" )) println (strings.Index(s, "123.com" )) println (strings.Repeat("Golang" , 4 )) println (strings.Replace(s, "123.com" , "163.com" , -1 )) println (strings.ReplaceAll(s, "c" , "%^" )) println (strings.Trim(s, "a" )) println (strings.Trim(" abc " , " " )) fmt.Println(strings.Fields("a abc " )) println (strings.HasPrefix("abc" , "ab" )) println (strings.HasSuffix("abc" , "bc" ))
拼音模糊匹配
1 go get github.com/axgle/pinyin
1 println(pinyin.Convert("hello,世界!")) // hello,ShiJie!
解决中文乱码
出现中文乱码的原因一般是因为字符编码有问题导致,主要是GBK和UTF8编码导致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // GBK to UTF8 func GbkToUtf8(s []byte) ([]byte, error) { reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder()) d, err := ioutil.ReadAll(reader) if err != nil { return nil, err } return d, err } // UTF8 to GBK func Utf8ToGbk(s []byte) ([]byte, error) { reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewEncoder()) d, err := ioutil.ReadAll(reader) if err != nil { return nil, err } return d, err }
推荐阅读:
bufio包系列之读取原理