一、打开和关闭文件
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))
}
}