Go语言学习23-文件操作——写文件
关于文件句柄的极简解释
1.只有windows中才有句柄,windows中的句柄是指针的指针,因为windows中对象的经常会在内存中移动(如进行垃圾回收后),所以地址值经常会变,所以就对外提供一个指针的指针即句柄给用户,句柄的地址是不会变的。
2.linux中是没有文件句柄的,只有文件描述符,只是大家习惯把它说成句柄。
3,linux中, 每当进程打开一个文件时,系统就为其分配一个唯一对应的整型文件描述符(从0开始),用来标识这个文件。linux 操作系统通常对每个进程能打开的文件数量有一个限制。默认是1024。
4、可以简单理解文件句柄就是一个对象。
写文件
之前读文件的时候,我们使用的是os.Open()
这个方法,这个函数只能用来读文件,不能用来写文件。
如果要写文件,就需要用到os.OpenFile()
这个方法
func OpenFile(name string,flag int,perm FileMode)(*File,error){
...
}
name
:文件名称
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 |
追加 |
再写案例之前,我们先来理解一个问题,即二进制运算,位运算符——或
假设我想要输出吃饭睡觉打豆豆,都输出,如何操作?
package main
import "fmt"
const (
eat int = 4
sleep int = 2
dadd int = 1
)
//左边为1 表示 吃饭 100 4
//中间为1 表示 睡觉 010 2
//右边为1 表示 打豆豆 001 1
func f(arg int) {
fmt.Printf("%b\n", arg)
}
func main() {
f(eat | dadd) //101 或计算就是,二进制不同时,位数为1
f(eat | sleep | dadd) //111 同理同上
}
文件写入操作案例
还记得读取文件吧,有三种写法,那么写入文件,同理也有三种写法。
第一种方式——
//打开文件写入内容
func main() {
fileobj, err := os.OpenFile("./xx.txt", os.O_APPEND|os.O_CREATE, 0644)
//file, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666) 这里就是创建+清除+只写入
if err != nil {
fmt.Println("err", err)
return
}
//这里有两种小的写入方法,一个是Write,一个是WriteString
fileobj.Write([]byte("虽然失败了,但是对后来的刘邦项羽起义打下了关键的基础。明修栈道,暗度陈仓。陈仓就是现在陕西宝鸡那里。项羽本来不想打了,划分界限,我做我的西楚霸王,给你封个汉王,你去做你的汉王吧!"))
fileobj.WriteString("天若有情天亦老,人间正道是沧桑!")
fileobj.Close()
}
//或者输出一个n也是可以的,看你的最终参数接收了
func main() {
fileobj, err := os.OpenFile("./xx.txt", os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
fmt.Println("err", err)
return
}
n, err := fileobj.Write([]byte("虽然失败了,但是对后来的刘邦项羽起义打下了关键的基础。明修栈道,暗度陈仓。陈仓就是现在陕西宝鸡那里。项羽本来不想打了,划分界限,我做我的西楚霸王,你做你的沛公吧!"))
if err != nil {
fmt.Println("err:", err)
return
}
fmt.Printf("已写入%d个字节!", n)
// fileobj.WriteString("天若有情天亦老,人间正道是沧桑!")
fileobj.Close()
}
第二种方式——bufio.NewWriter
将os.OpenFile()
打开的文件所生成的对象,传入bufio.NewWriter(fileobj)
注意一点,就是bufio
均为先创建一个缓冲区,即一个缓存,然后将内容先写入缓存中,最后将缓存放入硬盘中。所以这里就很容易出错,在后面一定要加上个
wr.Flush()
!!!不然会无法写入。这就类似于PHP了,PHP的文件上传就是通过move_uploaded_file
这个函数将缓存中的内容拷贝到文件中。
func writedemo2() {
fileobj, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("err", err)
return
}
defer fileobj.Close()
wr := bufio.NewWriter(fileobj)
wr.WriteString("holyshit!!!\n") //注意这里是写在缓存中的,而不是直接落盘的
wr.Flush() //将缓存的内容写入文件
}
func main() {
writedemo2()
}
第三种方式——ioutil.WriteFile
func writedemo3() {
str := "hellow bitch!"
err := ioutil.WriteFile("./xxx.txt", []byte(str), 0666)
if err != nil {
fmt.Println("err:", err)
return
}
}
利用bufio获取用户输入
普通获取输入的方法
可以看到这里我们输入的内容一旦有空格,它就会输出一个,是因为Scanln
它读到空白符(空格或者空行)就会停止。
利用bufio
package main
import (
"bufio"
"fmt"
"os"
)
//获取用户输入时有空格
func useScan() {
var s string
fmt.Println("请输入内容!")
fmt.Scanln(&s)
fmt.Printf("你输入的内容为:%s", s)
}
func usebufio() {
var s string
reader := bufio.NewReader(os.Stdin) //将标准输入创建成一个对象reader
fmt.Println("请输入内容!")
s, _ = reader.ReadString('\n') //对象reader的ReadString方法,一直读到换行符
fmt.Printf("你输入的内容为:%s", s)
}
func main() {
// useScan()
usebufio()
}
那么问题来了,我们既然可以将标准输入放入NewReader,那么是不是可以将我们的标准输入放入标准输出等,或者 将标准输出放入标准输入之类的。。
总结
三种写方式代码总结
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
//打开文件写入内容
func writedemo1() {
fileobj, err := os.OpenFile("./xx.txt", os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
fmt.Println("err", err)
return
}
n, err := fileobj.Write([]byte("虽然失败了,但是对后来的刘邦项羽起义打下了关键的基础。明修栈道,暗度陈仓。陈仓就是现在陕西宝鸡那里。项羽本来不想打了,划分界限,我做我的西楚霸王,你做你的沛公吧!"))
if err != nil {
fmt.Println("err:", err)
return
}
fmt.Printf("已写入%d个字节!", n)
// fileobj.WriteString("天若有情天亦老,人间正道是沧桑!")
fileobj.Close()
}
func writedemo2() {
fileobj, err := os.OpenFile("xx.txt", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
fmt.Println("err", err)
return
}
defer fileobj.Close()
wr := bufio.NewWriter(fileobj)
wr.WriteString("holyshit!!!\n") //注意这里是写在缓存中的,而不是直接落盘的
wr.Flush() //将缓存的内容写入文件
}
func writedemo3() {
str := "hellow bitch!"
err := ioutil.WriteFile("./xxx.txt", []byte(str), 0666)
if err != nil {
fmt.Println("err:", err)
return
}
}
func main() {
// writedemo1()
// writedemo2()
writedemo3()
}
练习
copyFile
借助io.Copy()
实现一个拷贝文件函数。
// CopyFile 拷贝文件函数
func CopyFile(dstName, srcName string) (written int64, err error) {
// 以读方式打开源文件
src, err := os.Open(srcName)
if err != nil {
fmt.Printf("open %s failed, err:%v.\n", srcName, err)
return
}
defer src.Close()
// 以写|创建的方式打开目标文件
dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
fmt.Printf("open %s failed, err:%v.\n", dstName, err)
return
}
defer dst.Close()
return io.Copy(dst, src) //调用io.Copy()拷贝内容
}
func main() {
_, err := CopyFile("dst.txt", "src.txt")
if err != nil {
fmt.Println("copy file failed, err:", err)
return
}
fmt.Println("copy done!")
}
实现一个cat命令
使用文件操作相关知识,模拟实现linux平台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))
}
}