设备文件有屏幕键盘等,标准输出就是屏幕,可以通过os.Stdout.Close()来阻止后面的程序输出内容,可以通过os.Stdint.Close()来阻止后面的程序通过键盘获取输入内容
文件的创建和写入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
path := "./test.txt"
//创建文件,如果文件存在则打开文件且清空文件内容,它返回一个file的指针和一个error
f, err := os.Create(path)
if err != nil {
//如果有错误就输出错误信息并且中止函数
fmt.Println(err)
return
}
//使用完毕记得要关闭文件,使用defer就可以在函数结束的前一刹那进行关闭
defer f.Close()
//Sprintln()可以将一行信息赋值给一个变量
buf := fmt.Sprintln("我往文件内写入东西")
//往文件内写入字符串,f代表文件的指针,WriteString就可以将内容追加到文件,注意是追加
//它返回两个值,一个是写入的字节数,另一个是error
n, err := f.WriteString(buf)
if err != nil {
fmt.Println(err)
return
}
|
![image.png](/p/golang%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C/media/image-5.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
fileObj,err:= os.OpenFile("./xxx.txt",os.O_CREATE|os.O_APPEND,0644)
if err!=nil{
fmt.Println(err)
return
}
fileObj.Write([]byte("强转string为字节切片"))
//也可以用bufio进行写操作
wr := bufio.NewWriter(fileObj)
//写到缓存中
wr.WriteString("hello\n")
//这一步是将缓存中的内容写入文件,如果没有这一步,内容是在缓存中的,等程序执行完毕就会消失
writer.Flush()
//也可以使用ioutil.WriteFile将内容写入文件
ioutil.WriteFile("./xx.txt",[]byte("写入的内容"),0666)
|
注意:在windows中,第三个参数文件权限可以随便写,因为这只针对于linux用户,os.O_CREATE|os.O_APPEND
表示没有文件就创建,有文件就打开,内容是追加写入(它的实现方式是这两个参数分别对应一个十六进制的数,转成2进制后根据数的值进行操作),如CREATE是0x00040,二进制是0100 0000,APPEND是0x00400,二进制是0100 0000 0000,使用| 位运算符表示两位有一个1就为1,即最后的值是0100 0100 0000,这样就实现了传一个int参数有多个功能,可以进行多个位运算符。
像只读和只写就是在同一位上一个为0一个为1,这样最后这一位就为1,只执行值为1的那个操作,就像linux的权限一样,每个位置的1代表不同的意义
os.O_CREATE|os.O_APPEND
和 os.O_CREATE|os.O_TRUNC
APPEND表示追加写入,TRUNC表示清空后写入
文件的读取
下面的代码可以实现指定读取多少内容,如果想直接读完整个文件,只需要在读取那里加一个for循环,它就会一直往下读取直到结尾出现error抛出EOF错误(EOF表示结尾),注意:抛出EOF的if语句要写到err !=nil中,因为EOF错误是它的子集,在err不为空中加return,而EOF中只需要加break,因为break触发后直接跳出循环了,就不会触发return了
尽量将关闭文件的defer写到err判定后面,因为如果先写defer,如果出现了err错误程序返回的*File是对应的零值nil,当程序结束时,defer中对于nil进行Close()函数就会造成panic错误(使用文件操作这里不会报错,因为close会返回一个error,但是如果调用的是第三方函数库,可能就会因此产生panic),而在err中写入return语句后,如果出现了err错误的话,直接返回,并不会执行到下面defer中的Close()函数
1
2
3
4
5
6
7
|
if err1 != nil {
if err1 == io.EOF{
break
}
fmt.Println(err1)
return
}
|
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
|
path := "./test.txt"
//打开文件,返回一个文件指针和一个error
f, err := os.Open(path)
if err != nil {
fmt.Println(err)
}
//函数运行结束时关闭文件
defer f.Close()
//创建一个2k长度的字节切片,表示读取的总量
buf := make([]byte, 1024*10)
//Read返回两个值,一个是字节数,一个是error
//n是读取了多少文件字节数,如果buf大于文件总字节数,n返回总字节数。
//如果总字节数大于buf,则n等于buf,即最多只能读取buf字节的内容,如果想多读取点就只能增大buf的长度值
//Read()方法会将读取的内容放入buf中,后面查看buf内容即是查看文件内容
n, err1 := f.Read(buf)
if err1 != nil && err1 != io.EOF { //文件出错并且没有读到结尾
fmt.Println(err1)
return
}
//可以用m:n来读取buf中间内容,n不能大于buf前面设定的值(即不能大于总长度),否则会报错
//n可以设置常量,即表示查看buf容量中从m到n的内容
//设置n则可以实现当文件总字节数小于buf量全部读取,大于则读取设置的最大值
fmt.Print("buf:", string(buf[:n]))
//buf中的值都是字节型的,可以通过强转变成string
//不用println是原因是ln会进行换行,如果第一次读取并没有读取到行末尾,
//ln还是会让它换行,这会导致读取内容有问题
|
如何解决fmt.Scanln()无法读取输入空格后的内容
Scanln()是以空白符分隔的,空白符就包括空格 回车等,如果输入的内容中含有空格,就只能读取到空格以前的内容,因此可以使用bufio来获取标准输入
1
2
3
4
5
|
fmt.Println("请输入内容:")
reader := bufio.NewReader(os.Stdin)
//一直读,读到\n结束,返回读取到的内容
str, _ := reader.ReadString('\n')
fmt.Println("你输入的内容是:", str)
|
如何一行一行的进行读取且务必读完文件的全部内容(不论大小)
bufio包有一个NewReader函数可以为文件的读取创建一个缓冲区并返回一个缓冲区的指针
ReadBytes是缓冲区 *Reader的方法,读取缓冲区直到第一次遇到delim字节(即指定的关键值’\n’等),读取出一次后缓冲区中对应的内容就会消失,因此可以用for死循环来读取多行缓冲区内容
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
|
path := "./test.txt"
//打开文件,返回一个文件指针和一个error
f, err := os.Open(path)
if err != nil {
fmt.Println(err)
}
//函数运行结束时关闭文件
defer f.Close()
//新建一个缓冲区,先把内容放进缓冲区里
r := bufio.NewReader(f)
for {
//遇到\n就结束读取,buf为读取到的数据,这种会将\n也读取进去,所以使用Printf()
buf, err1 := r.ReadBytes('\n')
//当读到结尾即退出循环
fmt.Printf("%v", string(buf))
/*把err判断放到打印后面的原因是如果将它放在前面,它会先判断是否是最后一行,当处于最后
一行时它直接跳出循环,最后一行的输出语句就不会进行,那么就会少输出一行
*/
if err1 != nil {
if err1 == io.EOF {
break
}
fmt.Println(err1)
}
}
|
使用ioutil直接读取整个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//它的底层用的依然是os.Open()的原理,文件的打开和defer关闭在底层已经设置好了,直接使用就行
func fileByioutil(){
b,err:=ioutil.ReadFile("C:\\Users\\admin\\Desktop\\新建文件夹\\gold_control_config.xml")
if err ==io.EOF{
fmt.Println("读取完了")
return
}
if err!=nil{
fmt.Println("file err",err)
return
}
fmt.Println(string(b))
}
|
实例:拷贝文件
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
//获取命令行参数,判断是否为3个
list := os.Args
if len(list) != 3 {
fmt.Println("拷贝格式为xx.exe src dst")
}
//比较目标文件和源文件是否同名
srcName := list[1]
dstName := list[2]
if srcName == dstName {
fmt.Println("源文件和目标文件不能相等")
}
//只读方式打开源文件
sf, err1 := os.Open(srcName)
if err1 != nil {
fmt.Println(err1)
return
}
//新建目标文件
df, err2 := os.Create(dstName)
if err2 != nil {
fmt.Println(err2)
return
}
//操作完毕要关闭文件
defer sf.Close()
defer df.Close()
for {
//读源文件
buf := make([]byte, 1024*4)
n, err3 := sf.Read(buf) //从源文件读取内容
if err3 != nil {
if err3 == io.EOF { //源文件读取完毕
break
}
fmt.Println(err3)
return
}
//写入目标文件,读多少写多少
_, err4 := df.Write(buf[:n])
if err4 != nil {
fmt.Println(err3)
return
}
}
|
如何在一个文件中间部分插入内容
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
|
//打开源文件,第一次读取到要插入的点
//读取完后下次读取时光标会在此处
f1,_:=os.Open("./a.txt")
buf:=make([]byte,1)
n,_:=f1.Read(buf)
//创建临时文件,并将读取的内容写入进去
f2,_:=os.Create("./b.txt")
f2.Write(buf[:n])
//写入自己需要插入的内容
f2.Write([]byte("哈哈哈哈哈"))
//读取源文件后面的部分,如果多就需要使用for循环
buf1:=make([]byte,1024)
n1,err:=f1.Read(buf1)
//将源文件后半部分写入临时文件
f2.Write(buf1[:n1])
if err!=nil{
return
}
f2.Close()
f1.Close()
//现在f2临时文件就有插入想要的内容,通过rename重命名成源文件实现覆盖
os.Rename("./b.txt","./a.txt")
|