Go -- 文件操作

一、打开和关闭文件

  • os.Open()函数能够打开一个文件,返回一个*File和一个err。对得到的文件实例调用close()方法能够关闭文件。

二、读取文件

1、file.Read()读取文件

  • 它接收一个字节切片,将读取的数据暂存切片中,返回读取的字节数和可能的具体错误,读到文件末尾时会返回0和io.EOF。
  • 这种指定字节读取有个缺陷,如中文占3个字符,进行切片时可能就会乱码

示例:

func main() {
	// 打开文件
	fileobj, err := os.Open("./main.go")
	if err != nil {
		fmt.Printf("open file failed, Err:%v", err)
		return
	}
	// 记得关闭文件
	defer fileobj.Close()
	// 读取文件
	var tmp [128]byte // 定义一个元素类型为byte容量为128的数组,用来存放读取的数据
	for {
		/* fileobj.Read()方法的()内传入的是byte类型的数组或切片作为容器,将数据存放在数组或者切片中.
		比如这里ttmp[:64]表示一次存放64字节,[:]那就是全部放满128字节
		 fileobj.Read()方法会返回读取的值以及报错信息 */
		n, err := fileobj.Read(tmp[:64])
		if err != nil {
			fmt.Println("read file failed , err:%v", err)
			return
		}
		fmt.Printf("读了 %d 个字节", n)
		fmt.Println(string(tmp[:n]))
		if n < 64 {
			return
		}
	}
}

2、bufio读取文件

  • bufio是在file的基础上封装了一层API,支持更多的功能。
  • buffio需要声明一个实例对象,利用对象去掉用buffio中封装的方法

示例

func readFromBuffio() {
	fileobj, err := os.Open("./main.go")
	if err != nil {
		fmt.Printf("文件打开失败,err:%v", err)
		return
	}
	defer fileobj.Close()
	// 声明一个读取文件的对象
	reader := bufio.NewReader(fileobj)
	for {
	// ReadString()方法同样返回两个值,数据和报错信息,()内传入的是分隔符,可以指定行为分割符,注意:传入的是字符
		line, err := reader.ReadString('\n') // 注意,()内传入的是字符,不是字符串,因此是单引号,表示以字符串的方式读取,按照'\n'一行行读取。
		if err == io.EOF { // 如果报错信息为io.EOF表示读取结束了,没有行分割符了
			if len(line) != 0 {  // 判断最后一行是否有数据,有则打印
				fmt.Print(line)
			}
			fmt.Println("文件读取结束")
			break
		}
		if err != nil {
			fmt.Printf("读取文件失败,err:%v", err)
			return
		}
		fmt.Print(line)
	}
}
func main() {
	readFromBuffio()
}

3、ioutil读取整个文件

  • io/ioutil包的ReadFile方法能够读取完整的文件,只需要将文件名作为参数传入。
func readFeilFromIoutil() {
	// 利用ioutil.ReadFile()方法,传入文件名,直接打开读取文件内容
	content, err := ioutil.ReadFile("./main.go")
	if err != nil {
		fmt.Printf("read file falied,Err:%v\n", err)
		return
	}
	fmt.Println(string(content))
}
func main() {
	readFeilFromIoutil()
}

三、文件写入操作

  • os.OpenFile()函数能够以指定模式打开文件,从而实现文件写入相关功能。
    • 上面的os.Open()只能是只读方式打开
  • 源代码签名如下
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
	...
}
# 其中:
#    name:要打开的文件名 
#    flag:打开文件的模式。多个flag之间用|逻辑或隔开(文件模式底层使用的二进制数进行逻辑或运算来表示的)
#    perm:文件权限,一个八进制数。r(读)04,w(写)02,x(执行)01

文件的打开模式有以下几种:

模式含义
os.O_WRONLY只写
os.O_CREATE创建文件
os.O_RDONLY只读
os.O_RDWR读写
os.O_TRUNC清空
os.O_APPEND追加
  • 查看各种模式的定义
const (
	// Invented values to support what package os expects.
	O_RDONLY   = 0x00000     // 0000 0000 0000
	O_WRONLY   = 0x00001     // 0000 0000 0001
	O_RDWR     = 0x00002     // 0000 0000 0010
	O_CREAT    = 0x00040     // 0000 0100 0000
	O_EXCL     = 0x00080     // 0000 1000 0000
	O_NOCTTY   = 0x00100     // 0001 0000 0000
	O_TRUNC    = 0x00200     // 0010 0000 0000
	O_NONBLOCK = 0x00800     // 1000 0000 0000
	O_APPEND   = 0x00400     // 0100 0000 0000
	O_SYNC     = 0x01000     
	O_ASYNC    = 0x02000     
	O_CLOEXEC  = 0x80000     
)

# 可以看出各种模式时int类型的常量,16进制数据,转换成二进制进行逻辑或,可以看出,每个模式都不冲突(即1的位置来判断文件打开模式)
# 如 O_RDWR|O_CREAT = 0000 0100 0010,那么0000 0100 0010这个二进制数就代表即读写又创建

1、Write和WriteString

  • Write()写入字节切片数据
  • WriteString()直接写入字符串数据

示例;

func wirteFile() {
	// 打开文件
	file, err := os.OpenFile("./abc.txt", os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		fmt.Printf("file open failed, Err:%v\n", err)
		return
	}
	defer file.Close()
	// Write()方法,传入的byte类型的切片
	file.Write([]byte("你好!!\n"))
	// WriteString()方法,直接传入字符串
	file.WriteString("我是周知\n")
}

func main() {
	wirteFile()
}

2、bufio.NewWriter

  • bufio.NewWriter的用法和bufio.NewReader类型,需要先声明对象
func writeFileByBuffio() {
	file, err := os.OpenFile("./123.txt", os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		fmt.Printf("file open failed, Err:%v\n", err)
		return
	}
	defer file.Close()
	// 同样需要声明一个写的对象,NewWriter()方法中传入的是文件句柄
	writer := bufio.NewWriter(file)
	writer.Write([]byte("Hello "))   // Write()方法传入的数据时byte的切片
	writer.WriteString("World !!")   // WriteString()方法直接差混入字符串
	writer.Flush()    // 注意:文件写入操作,要执行下Flush()后,将缓存中的数据写入文件,操作才算完成
}

func main() {
	writeFileByBuffio()
}

3、ioutil.WriteFile

  • 直接进行写入操作

示例:

func writeFileByIoutil() {
	str := "你好,我是小猪佩奇!!\n我要吃了你"
	err := ioutil.WriteFile("./new.txt", []byte(str), 0644)
	if err != nil {
		fmt.Printf("file open failed, Err:%v\n", err)
		return
	}
}
func main() {
	writeFileByIoutil()
}

4、利用io.Copy实现文件复制

  • io.Copy()方法传入的读和写两个对象
    查看源代码
func Copy(dst Writer, src Reader) (written int64, err error) {
	return copyBuffer(dst, src, nil)
}

示例:

func CopyFile(destFile, srcFile string) (written int64, err error) {
	srcfileobj, err := os.Open(srcFile)
	if err != nil {
		fmt.Printf("文件打开失败,err:%v", err)
		return
	}

	destFileobj, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		fmt.Printf("文件打开失败,err:%v", err)
		return
	}
	defer destFileobj.Close()
	defer srcfileobj.Close()
	return io.Copy(destFileobj, srcfileobj)
}

func main() {
	CopyFile("./test.txt", "./main.go")
}

5、利用buffio获取用户输入带空格的字符串

  • 当我们直接fmt.Scanln()方法让用户输入字符串时,发现无法输入带有空格的字符串,原因是fmt.Scanln()会默认以空格回车为分割符,因此输出的永远只有第一个空格前的字符

示例如下:当用户输入带空格字符,例如: a b c,返回的结果却只有a

func main() {
	var str string
	fmt.Print("请输入:")
	fmt.Scanln(&str)
	fmt.Println(str)
}

输出结果

请输入:a b c
a
  • 因此,需要利用buffio的方法进行优化
    示例:利用 os.Stdin 来获取标准输入,将输入的内容以换行为分割
func main() {
	var str string
	fmt.Print("请输入:")
	reader := bufio.NewReader(os.Stdin)
	str, _ = reader.ReadString('\n')
	fmt.Println(str)
}

练习

  • 实现一个cat命令
package main

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

// cat命令实现
func cat(r *bufio.Reader) {
	for {
		buf, err := r.ReadBytes('\n') //注意是字符
		if err == io.EOF {
			// 退出之前将已读到的内容输出
			fmt.Fprintf(os.Stdout, "%s", buf)
			break
		}
		fmt.Fprintf(os.Stdout, "%s", buf)
	}
}

func main() {
	flag.Parse() // 解析命令行参数
	if flag.NArg() == 0 {
		// 如果没有参数默认从标准输入读取内容
		cat(bufio.NewReader(os.Stdin))
	}
	// 依次读取每个指定文件的内容并打印到终端
	for i := 0; i < flag.NArg(); i++ {
		f, err := os.Open(flag.Arg(i))
		if err != nil {
			fmt.Fprintf(os.Stdout, "reading from %s failed, err:%v\n", flag.Arg(i), err)
			continue
		}
		cat(bufio.NewReader(f))
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值