Golang学习笔记(09-5-文件读写)

464 阅读5分钟

1. 常用方法和函数

## os 模块
1.  File结构体
    Syntax: type File struct {  }
    Man:    文件对象
    
2.  Open()
    Syntax: func Open(name string) (file *File, err error)
    Man:    Open打开一个文件用于读取,对应的文件描述符具有O_RDONLY模式

3.  OpenFile()
    Syntax: func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
    Man:    打开文件,可以指定打开方式、权限等。一般应用于写文件的场景
            flag使用位运算中的 | 拼接:
                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  // 如果可能,打开时清空文件
                )
            perm是Linux下文件权限,如 0644, 04003.  Close()
    Syntax: func (f *File) Close() error
    Man:    Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。
   
4.  Name()
    Syntax: func (f *File) Name() string
    Man:    Name方法返回(提供给Open/Create等方法的)文件名称

    
## bufio模块
1.  Reader结构体
    Syntax: type Reader struct {  }
    Man:    Reader实现了给一个io.Reader接口对象附加缓冲

2.  NewReader()
    Syntax: func NewReader(rd io.Reader) *Reader
    Man:    创建默认缓冲区大小的 Reader 对象指针,默认为 2048

3.  NewReaderSize()
    Syntax: func NewReaderSize(rd io.Reader, size int) *Reader
    Man:    创建指定缓冲区大小的 Reader 对象指针
    
4.  ReadString()
    Syntax: func (b *Reader) ReadString(delim byte) (line string, err error)
    Man:    ReadString读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的字符串。
            如果ReadString方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。
            当且仅当ReadString方法返回的切片不以delim结尾时,会返回一个非nil的错误。

5.  ReadBytes()
    Syntax: func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
    Man:    ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。
            如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。
            当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。

6.  NewWriter()
    Syntax: func NewWriter(w io.Writer) *Writer
    Man:    NewWriter创建一个具有默认大小缓冲、写入w的*Writer
    
7.  NewWriterSize()
    Syntax: func NewWriterSize(w io.Writer, size int) *Writer
    Man:    NewWriterSize创建一个具有最少有size尺寸的缓冲、写入w的*Writer。
    
8.  WriteString()
    Syntax:func (b *Writer) WriteString(s string) (int, error)
    Man:    WriteString写入一个字符串,返回写入的字节数。

9.  Write()
    Syntax: func (b *Writer) Write(p []byte) (nn int, err error)
    Man:    Write将p的内容写入缓冲。返回写入的字节数。如果返回值nn < len(p),还会返回一个错误说明原因。

10. Flush()
    Syntax: func (b *Writer) Flush() error
    Man:    刷盘,将系统缓冲区内容写入文件

## ioutil模块
1.  ReadFile()
    Syntax: func ReadFile(filename string) ([]byte, error)
    Man:    ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF

2.  WriteFile()
    Syntax: func WriteFile(filename string, data []byte, perm os.FileMode) error
    Man:    函数向filename指定的文件中写入数据。
            如果文件不存在将按给出的权限创建文件,否则在写入数据之前清空文件。

2. 读文件

2.1. 带缓冲区的方式读取文件

使用缓冲区的方式读取文件内容,适合文件较大的情况或者文件大小未知的情况,避免一次性占用太多内存。

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main()  {
	file, err := os.Open("/etc/passwd")
	if err != nil {
		fmt.Printf("读取文件失败,err: %v\n", err)
		return
	}
	defer file.Close()
	// 使用缓冲区的方式读取文件内容,适合文件内容较大的情况,避免一次性占用太多内存
	reader := bufio.NewReaderSize(file, 2048)
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF {
			break
		} else if err != nil {
			fmt.Println("文件读取异常,err=", err)
		} else {
			fmt.Print(str)
		}
	}
}

2.2. 一次性读取文件内容

一次性读取文件内容,适合小文件,如配置文件等。

package main

import (
	"fmt"
	"io/ioutil"
)

func main()  {
	// 一次性读取文件内容,适合小文件,如配置文件等
	if content, err := ioutil.ReadFile("/etc/passwd"); err != nil {
		fmt.Println("文件读取失败")
	} else {
		fmt.Print(string(content))
	}
}

3. 写文件

3.1. 带缓冲的方式写文件

使用带缓冲的方式写文件实际运用场景比较多,需要注意的以下问题:

  • 必须要关闭文件前使用 writer.Flush() ,否则可能导致写入的文件内容不全
  • 写入字节切片 []byte 和 string 是一样的效果,不会出现中文乱码问题
  • 在Linux下,如果当前用户对文件没有写入权限如0400权限下,会提示 permission denied 
  • 写文件时是覆盖还是追加,取决于打开文件时使用的 flag

3.1.1. 追加文件内容

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main()  {
	file, err := os.OpenFile("test.txt", os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0600)
	if err != nil {
		fmt.Println("打开文件失败; err=",err)
		return
	}
	defer func() {
		_ = file.Close()
	}()
	// 使用缓冲的方式写字符串到文件
	writer := bufio.NewWriter(file)
	for i:=0; i<3; i++ {
		if _, err := writer.WriteString("Hello 北京"+"\n"); err != nil {
			fmt.Println("文件写入失败, err=", err)
			break
		}
	}
	// 使用缓冲的方式写byte切片到文件
	sli := []byte("Hello 北京\n")
	for i:=0; i< 3; i++ {
		if _, err := writer.Write(sli); err != nil {
			fmt.Println("文件写入失败, err=", err)
			break
		}
	}
	_ = writer.Flush()
}

3.1.1. 覆盖文件内容

想要覆盖文件,必须要添加 os.O_TRUNC ,否则会和原有内容重叠。

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main()  {
	file, err := os.OpenFile("test.txt", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0600)
	if err != nil {
		fmt.Println("打开文件失败; err=",err)
		return
	}
	defer func() {
		_ = file.Close()
	}()
	for i:=0; i<3; i++ {
		if _, err := writer.WriteString("Hello 南京"+"\n"); err != nil {
			fmt.Println("文件写入失败, err=", err)
			break
		}
	}
	_ = writer.Flush()
}

3.2. 一次性写文件

一次性写入文件内容,比如调用go生成k8s的清单文件等。

package main

import (
	"fmt"
	"io/ioutil"
)

func main()  {
	err := ioutil.WriteFile("go.txt", []byte("Golang 不难!"), 0400)
	if err != nil {
		fmt.Println("文件写入失败,err=", err)
	}
}

3.3. 判断文件是否存在并写入

在下面的案例中,由于涉及 ssh-key,因此覆写时,需要修改文件权限,避免写入失败!

package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

func FileIsExist(filename string) (bool, error) {
	if _, err := os.Stat(filename); err == nil {
		_ = os.Chmod("go.txt", 0600)
		return true, nil
	} else if os.IsNotExist(err) {
		return false, nil
	} else {
		return false, err
	}
}

func main()  {
	if flag, _ := FileIsExist("go.txt"); ! flag {
		fmt.Println("文件不存在")
		return
	}
	if err := ioutil.WriteFile("go.txt", []byte("ssh-key"), 0400); err != nil {
		fmt.Println("文件写入失败,err=",err)
	}
}