文件与 IO 操作

文件与 IO 操作1 文件读取&文件的写入1.1 文件的打开1.2 不带缓冲对文件的读取1.2.1 一次性读取全部内容1.3 创建文件和不带缓存写入内容操作1.3.4 通过命令行输入到文件中1.3.5 在程序中格式化输入内容到文件2 OpenFile 和 文件位置操作2.1 如果文件不存在就创建,并追加内容2.2 文件只读操作2.3 文件只写操作2.4 文件读写操作(仅供了解)2.5 文件位置将光标移动到最前面操作2.6 光标指定向前移动次数范例2.7 将光标移动到最前面写入内容3 带缓冲IO3.1 带缓冲的读取3.1.1 Read()方法读取3.1.2 ReadByte() 方法3.1.3 ReadBytes() 读取到固定字节结束3.1.4 ReadLine() 换行读取读取一行3.1.5 ReadSlice() 读取到固定字节结束3.1.6 ReadString() 和 ReadBytes() 类似,返回 string3.1.7 WriteTo() 将输入输出3.2 带缓存的输入3.3 带缓冲的写3.3.1 WriteString() 写入 string 类型4 文件目录操作4.1 获取文件信息4.2 读取目录信息4.3 获取指定目录信息4.4 对文件的常规操作4.4.1 文件/文件夹移动4.4.2 删除文件4.4.3 创建目录5 io工具&文件路径5.1 ioutil 包5.1.1 ReadAll() 读取文件中全部内容5.1.2 ReadFile() 返回文件所有内容5.1.3 ReadDir() 返回所有文件夹的内容5.1.4 WriteFile() 写入数据到文件中5.2 对文件路径进行处理5.2.1 filepath 包5.2.1.1 filepath.Abs() 获取文件绝对路径5.2.1.2 filepath.Base() 获取自身文件名5.2.1.3 filepath.Dir() 获取父目录5.2.1.4 filepath.Split() 分割5.2.1.5 filepath.Ext() 返回文件后缀5.2.1.6 filepath.HasPrefix() 以什么为前缀5.2.1.7 filepath.IsAbs() 判断是否为绝对路径5.2.1.8 filepath.Glob() 用于文件匹配5.2.1.9 filepath.Walk() 将所有目录文件全部输出5.2.1.10filepath.Join() 用来连接多个路径6 文件格式(编解码)6.1 编码(持久化的过程)6.1.1 gob 持久化范例6.1.1.1 gob 持久化写入范例(编码)6.1.1.2 gob 读取范例(解码)6.1.2 CSV 格式6.1.2.1 CSV 格式编码6.1.2.2 CVS 格式解码7 作业7.1 复制文件(只是文件)7.2 作业 27.3 作业3 7.4 作业47.5 作业5

文件与 IO 操作

基本操作(不带缓冲 IO):读、写

标准输入、输出、错误

带缓冲的 IO

文件,对我们并不陌生,文件是数据源(保存数据的地方)的一种,比如大家经常使用的word文档,txt文件,excel文件…都是文件。文件最主要的作用就是保存数据,它既可以保存一张图片,也可以保持视频,声音…

程序的路径:

image-20210619174951288

文件类型:

我们在处理文本文件的时候可以用 string 去处理,也可以用 byte 去处理

但是在处理二进制文件的时候一定要用[] byte去处理

image-20210619175305082

1 文件读取&文件的写入

在文件程序中是以流的形式来操作的。

image-20210427162913831

流:数据在数据源(文件)和程序(内存)之间经历的路径,经常操作的有两种流如下

输入流:数据从数据源(文件)到程序(内存)的路径(也就是当我们读取文件的时候,输入流往往对应的是读的操作

输出流:数据从程序(内存)到数据源(文件)的路径(比如我们把内存中的数据重新写回文件,这个流就是从内存流向文件中,输出流往往对应的是写文件

1.1 文件的打开

我们在对文件操作的时候需要使用到 os

打开文件使用到 os.Open(name string) (*File,error)

package main

import (
    "fmt"
    "os"
)

func main() {
    filePath := "test.txt"          // 必须是执行二进制程序所在的的当前目录下
    file, err := os.Open(filePath)
    fmt.Println(file, err)
}

输出

[18:01:45 root@go filereader]#go run main.go 
<nil> open test.txt: no such file or directory

# 输出 没有找到 test.txt 这个文件 , 因为当前目录中就没有 test.txt 文件

然后我在当前目录创建一个 test.txt 文件

image-20210619180916910

再次输出就不会报错了

[18:06:04 root@go filereader]#go run main.go 
&{0xc0000bc120} <nil>

1.2 不带缓冲对文件的读取

先查看 os.File 这个结构体有哪些方法

image-20210619181847900

对文件的话就使用 func (f *File) Read(b []byte) (n int, err error) 方法

  1. 我们先在当前目录下创建一个 test.txt 文件

    image-20210619184008098

[18:39:21 root@go filereader]#touch test.txt

# 写入内容 
12345678
  1. 然后在编写读出程序

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
        // 打开文件操作
        filePath := "test.txt"
        file, err := os.Open(filePath)
        if err != nil {
            fmt.Println(err)
        }
        // 创建一个 content 的 byte 切片,长度为 3 ,也就是说一次能够读取三个字节
        content := make([]byte, 3)
    
        // 把文件的内容处理之后放到了 content 切片中了
        // 第一次读取
        fmt.Println(file.Read(content))
        fmt.Printf("read1:%q\n", content)
    
        // 第二次读取
        fmt.Println(file.Read(content))
        fmt.Printf("read2:%q\n", content)
    
        // 第三次读取
        fmt.Println(file.Read(content))
        fmt.Printf("read3:%q\n", content)
    
        // 第四次读取
        fmt.Println(file.Read(content))
        fmt.Printf("read4:%q\n", content)
    }
    

    输出:

    [18:57:48 root@go filereader]#go run main.go 
    3 <nil>
    read1:"123"
    3 <nil>
    read2:"456"
    2 <nil>
    read3:"786"
    0 EOF
    read3:"786"
    
    # 我们发现 error 的值变为了 EOF 而不在是 nil
    # EOF 用来标识文件读取结束了

    image-20210619184052730

他在每次读取的时候都会替换掉前面的字节内容,在第三次读取的时候只读取了两个字节,所以最后一个数就是 6

1.2.1 一次性读取全部内容

由上面的操作范例,我们可以看到读取文件其实每次都是一个重复循环的过程

package main

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

func main() {
    // 打开文件操作
    filePath := "test.txt"
    file, err := os.Open(filePath)
    if err != nil {
        fmt.Println(err)
        return
    }

    // 最后关闭文件句柄,以防止内存溢出,因为防止程序中间发生错误
    defer file.Close()
    
    // 字节长度为 3 
    content := make([]byte, 3)

    // 通过 for{} 读取文件内容
    for {
        n, err := file.Read(content)

        // 因为文件读取完毕之后输出 err = EOF 而不再是 nil 值就退出当前 for 循环
        if err != nil {
            if err != io.EOF { // EOF => 标识文件读取结束了,非 EOF 结束
                fmt.Println(err)
            }
            break
        }

        // 输出 content[:n] 中的内容,每次输出到切片的最后,(文件内容存在 content 切片中)
        // 因为使用 切片 获取的时候底层是共享同一个内存空间的
        fmt.Println(string(content[:n]))
    }
}

输出:

[19:56:18 root@go filereader]#go run main.go 
123
456
78

文件中有中文的处理输出:

通过输出,依旧能处理文件内容为中文的文件

因为中文编码为的 Unicode 码,而且正好一个 Unicode 码占 3 个字节

所以为啥 content := make([]byte, 3) 的长度要为 3 了,因为读取中文的长度

[20:07:38 root@go filereader]#go run main.go 
一
二
三
四
五
六
七
八
九

如果我们需要用对 string 的方式进行处理,或者说对文本的方式进行处理,我们用的最多的是带缓冲的 io

1.3 创建文件和不带缓存写入内容操作

image-20210619211041887

使用 os.Create 方法创建一个相对路径的 text.txt 文件

范例:

package main

import (
	"fmt"
	"os"
)

func main() {
	// 创建文件路径
	path := "text.txt"

	// 使用 os.Create() 方法
	file, err := os.Create(path)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()

	// 通过 Write() 方法写入数据
	file.Write([]byte("[]byte切片\n"))

	// 输出写入的长度和错误信息
	fmt.Println(file.Write([]byte("12345\n")))

	// 通过 WriteString() 写入的是 string 类型
	file.WriteString("我是 writeString 写入")
}

写入内容:

image-20210619212341794

1.3.4 通过命令行输入到文件中

标准输入 => 命令行的文件 os.Stdin 标准输出 => 命令行的文件 os.Stdout 标准错误输出 => 命令行的文件 os.Stderr

package main

import (
	"fmt"
	"os"
)

func main() {
    // 创建文件
	file, err := os.Create("text.txt")
	if err != nil {
		fmt.Println(err)
		return
	}

    // 关闭文件
	defer file.Close()

    // 如何内容
	name := ""
	fmt.Print("请输入需要写入到文件中的内容:")
	fmt.Scanln(&name)

    // 写入内容到文件
	file.WriteString(name)

}

输出:

[21:30:51 root@go stdio]#go run main.go 
请输入需要写入到文件中的内容:在黑夜中用光明照亮永恒

image-20210619213151045

1.3.5 在程序中格式化输入内容到文件

在程序代码中将写入的内容通过格式化输入到文件中

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Create("text.txt")
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()

	name := "ll"
	fmt.Fprintf(file, "i am %s", name)

}

没有输出内容

写入到文件后的内容

image-20210619213554584

2 OpenFile 和 文件位置操作

image-20210621165608661

在写文件的时候要使用到 OpenFile 函数,并且这里面有三个参数,然后整个 openfile 函数会返回两个返回参数,一个是文件指针,一个是 err ,也就是说打开文件错误就会返回 err

第一个参数 name :

要把这个内容写入到那个文件里面去

第二个参数 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  // 如果可能,打开时清空文件
)

第三个参数 FileMode:

打开文件权限

type FileMode(在 windows 下无效,只能在 unix 和 linux 下有用)

type FileMode uint32

FileMode代表文件的模式和权限位。这些字位在所有的操作系统都有相同的含义,因此文件的信息可以在不同的操作系统之间安全的移植。不是所有的位都能用于所有的系统,唯一共有的是用于表示目录的ModeDir位。

const (
    // 单字符是被String方法用于格式化的属性缩写。
    ModeDir        FileMode = 1 << (32 - 1 - iota) // d: 目录
    ModeAppend                                     // a: 只能写入,且只能写入到末尾
    ModeExclusive                                  // l: 用于执行
    ModeTemporary                                  // T: 临时文件(非备份文件)
    ModeSymlink                                    // L: 符号链接(不是快捷方式文件)
    ModeDevice                                     // D: 设备
    ModeNamedPipe                                  // p: 命名管道(FIFO)
    ModeSocket                                     // S: Unix域socket
    ModeSetuid                                     // u: 表示文件具有其创建者用户id权限
    ModeSetgid                                     // g: 表示文件具有其创建者组id的权限
    ModeCharDevice                                 // c: 字符设备,需已设置ModeDevice
    ModeSticky                                     // t: 只有root/创建者能删除/移动文件
    // 覆盖所有类型位(用于通过&获取类型位),对普通文件,所有这些位都不应被设置
    ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice
    ModePerm FileMode = 0777 						// 覆盖所有Unix权限位(用于通过&获取类型位)
)

这些被定义的位是FileMode最重要的位。另外9个不重要的位为标准Unix rwxrwxrwx权限(任何人都可读、写、运行)。这些(重要)位的值应被视为公共API的一部分,可能会用于线路协议或硬盘标识:它们不能被修改,但可以添加新的位。

2.1 如果文件不存在就创建,并追加内容

package main

import (
	"fmt"
	"os"
	"time"
)

func main() {

	// 多个模式之间可以通过 | 组合使用
	// os.O_WRONLY|os.O_CREATE|os.O_APPEND 这是写入如果没有 test.log 就创建同时向该文件追加内容
	// 文件权限为 0666
	file, err := os.OpenFile("test.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()

	// 追加日志系统的 logs 时间显示
	fmt.Fprintf(file, "%s\n", time.Now().Format("[2006-01-02 15:04:05]"))

}

创建后的 test.log 文件

image-20210621171732613

2.2 文件只读操作

package main

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

func Read() {
	FileName := "test.txt"

	// 通过 openfile 中的 os.O_RDONLY 只读模块,权限为 0666
	file, err := os.OpenFile(FileName, os.O_RDONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()

	// 读取文件的 []byte 切片
	str := make([]byte, 3)

	// 全量读取
	for {
		n, err := file.Read(str)
		if err != nil {
			if err != io.EOF {
				fmt.Println(err)
				break
			}
		}

		// 判断文件内容是否为空
		if n == 0 {
			break
		}
		fmt.Print(string(str[:n]))
	}
	
	// 写入内容,我爱祖国!!
	fmt.Println(file.Write([]byte("我爱祖国!!")))
}

func main() {
	Read()
}

执行程序

[18:25:31 root@go fileonly]#go run main.go 
0 write test.txt: bad file descriptor

# 0 write test.txt: bad file descriptor: 0 写入 错误的文件描述符

但是由于我们的在使用 os.OpenFile 函数的时候用的是使用的 os.O_RDONLY 只读模式,所以创建出来的 test.txt 文件里面的内容并不会被写入file.Write([]byte("我爱祖国!!"))

image-20210621182400188

test.txt 文件内容为空

image-20210621182514462

2.3 文件只写操作

package main

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

func Read() {
	FileName := "test.txt"

	// 通过 openfile 中的 os.O_RDONLY 只写模块,权限为 0666
	file, err := os.OpenFile(FileName, os.O_WRONLY, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()

	fmt.Println(file.Write([]byte("我爱祖国!!")))

	// 读取文件的 []byte 切片
	str := make([]byte, 3)

	// 全量读取
	for {
		n, err := file.Read(str)
		if err != nil {
			if err != io.EOF {
				fmt.Println(err)
				break
			}
		}

		// 判断文件内容是否为空
		if n == 0 {
			break
		}
		fmt.Print(string(str[:n]))
	}

	// 写入内容,我爱祖国!!

}

func main() {
	Read()
}

执行程序

[18:31:36 root@go fileonly]#go run main.go 
14 <nil>
read test.txt: bad file descriptor

# read test.txt: bad file descriptor: 读 test.txt 错误的文件描述符

2.4 文件读写操作(仅供了解)

package main

import (
	"fmt"
	"os"
)

func Read_Write(name string) {
	file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return
	}
	defer file.Close()

    // 写入内容
	file.Write([]byte("1234567"))

    // 读取内容
    srt := make([]byte, 10)
	fmt.Println(file.Read(srt[:]))
}

func main() {
	Read_Write("test.txt")
}

执行文件:

[18:43:29 root@go filereadwrtier]#go run main.go 
0 EOF

发现已经写入到了文件中

image-20210621185046201

但是为什么读取的时候会是 0 EOF 呢,因为在我们写入文件的时候他的执行光标已经到了 test.txt 文件的末尾,所以在读取的时候就会没有内容,那我们想要读取信息的话可以将在最后的光标移动到最前面,由前面开始读取到最后

见下面文件位置光标操作范例

2.5 文件位置将光标移动到最前面操作

范例:

使用 Seek()

image-20210621185924737

往前移动就是 -负数,往后移动就是正数。

package main

import (
	"fmt"
	"os"
)

func Read_Write(name string) {
	file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return
	}
	defer file.Close()

	file.Write([]byte("123"))

	srt := make([]byte, 10)

	// 第一个 0 表示文件的原始位置
	// 第二个 0 表示相对于当前偏移
	file.Seek(0, 0)
	fmt.Println(file.Read(srt))
	fmt.Println(string(srt))
}

func main() {
	Read_Write("test.txt")
}

执行

[19:04:42 root@go filereadwrtier]#go run main.go 
3 <nil>		# 有 7 个长度
123			# 文件内容

2.6 光标指定向前移动次数范例

image-20210621191441300

package main

import (
	"fmt"
	"os"
)

func Read_Write(name string) {
	file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return
	}
	defer file.Close()

	file.Write([]byte("123"))

	srt := make([]byte, 10)

	// 第一个 -2 表示文件的向前偏移位置
	// 第二个 1 表示相对于当前偏移
	file.Seek(-2, 1)
	fmt.Println(file.Read(srt))
	fmt.Println(string(srt))
}

func main() {
	Read_Write("test.txt")
}

执行:

[19:07:10 root@go filereadwrtier]#go run main.go 
2 <nil>
23

2.7 将光标移动到最前面写入内容

如果将光标移动到最前面,然后再写入内容的话,就会发现它会将所对应的内容覆盖掉

package main

import (
	"os"
)

func Read_Write(name string) {
	file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return
	}
	defer file.Close()

	// 写入 123
	file.Write([]byte("123"))

	// 第一个 0 表示文件光标移动到最开始的地方
	// 第二个 0 表示相对于当前偏移
	file.Seek(0, 0)

	// 写入 ab
	file.WriteString("ab")
}

func main() {
	Read_Write("test.txt")
}

执行之后查看文件内容,最前面的 12 被覆盖为了 ab

image-20210621192154030

3 带缓冲IO

带缓冲的执行速度比不带缓存的要快,因为在应用层是有一层缓存的

io 包主要提供对流的基本操作功能

a) 常用常量

  • EOF:表示文件读取结束

b) 常用函数

  • Copy: 将输出流复制到输入流中

  • CopyBuffer:将输出流复制到输出流中,同时拷贝到字节切片中

  • CopyN:从输入流中复制 N 个字节到输出流

  • WriteString:像输出流中写入字符串

3.1 带缓冲的读取

image-20210621215123776

func (b *Reader) ReadBytes(delim byte) ([]byte, error) 该方法是当读到某个字符以后才进行停止,或者说当文件结束的时候我就停止。

func (b *Reader) ReadRune() (r rune, size int, err error) 该方法就是可以处理中文的格式。比如每次读取一个 Unicode 的字符

func (b *Reader) ReadString(delim byte) (string, error) 该方法是当读到某个字符串以后才进行停止,或者说当文件结束的时候我就停止。

3.1.1 Read()方法读取

  1. 创建一个test.txt 文件,写入内容

    image-20210621220448540

  1. 编写代码

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    )
    
    func main() {
    	// 打开文件
    	filePath := "test.txt"
    	file, err := os.Open(filePath)
    	if err != nil {
    		return
    	}
    
    	// 关闭文件
    	defer file.Close()
    
    // 将文件读取到 NewReader 缓存中,并返回其缓冲区具有默认大小的新读取器,将文件内容写入 reader。
    	reader := bufio.NewReader(file)
    
    	// 创建 str 每次读取的 byte 数量
    	str := make([]byte, 3)
    	n, err := reader.Read(str)
        
        // 读取前三个
    	fmt.Println(n, err, str[:n])
    
    }

    执行:

    [22:03:54 root@go testreadr]#go run main.go 
    3 <nil> [49 50 51]
    
    # 由于没有转为 string 类型,所以 [49 50 51] 是对应的 Unicode 编码

3.1.2 ReadByte() 方法

该方法只能够一次读取一个字节

package main

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

func main() {
	// 打开文件
	filePath := "test.txt"
	file, err := os.Open(filePath)
	if err != nil {
		return
	}

	// 关闭文件
	defer file.Close()

// 将文件读取到 NewReader 缓存中,并返回其缓冲区具有默认大小的新读取器,将文件内容写入 reader。
	reader := bufio.NewReader(file)

	// ReadByte() (byte, error) 返回 byte 和 err
	n1, err1 := reader.ReadByte()
    
	fmt.Println(n1, string(n1), err1) // 转为 string 输出 n1
}

执行

[22:08:54 root@go testreadr]#go run main.go 
49 1 <nil>

# 49 为 1 的 Unicode 编码
# 1 就是转为 string 的 byte 类型
# nil = error 为空

3.1.3 ReadBytes() 读取到固定字节结束

ReadBytes(delim byte) ([]byte, error)

  1. 编写 test.txt 文件

    内容为:

    image-20210621221737573

  1. 当我们读取到内容中的 | 结束

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    )
    
    func main() {
    	// 打开文件
    	filePath := "test.txt"
    	file, err := os.Open(filePath)
    	if err != nil {
    		return
    	}
    
    	// 关闭文件
    	defer file.Close()
    
    // 将文件读取到 NewReader 缓存中,并返回其缓冲区具有默认大小的新读取器,将文件内容写入 reader。
    	reader := bufio.NewReader(file)
    
    	// reader.ReadBytes('|') 读取到中划线结束,并且将 | 读取
    	n, err := reader.ReadBytes('|')
    	if err != nil {
    		return
    	}
    	fmt.Printf("%v\n%v\n", n, string(n))
    
    	n1, err1 := reader.ReadBytes('|')
    	if err1 != nil {
    		return
    	}
    	fmt.Printf("%v\n%v\n", n1, string(n1))
    
    	n2, err2 := reader.ReadBytes('|')
    	fmt.Printf("%v\n%v\n%v\n", n2, string(n2), err2)
    }
    

    执行

    [22:24:29 root@go testreadr]#go run main.go 
    [49 50 51 52 53 124]
    12345|
    
    [54 55 56 124]
    678|
    
    [57 120 120 120 120 120 10 97 98 99 10 49 50 51 120 121 122 10 120 121 122]
    9xxxxx
    abc
    123xyz
    xyz
    EOF
    
    # EOF 由于最后一次读取完毕所有 err 就为 EOF

我们可以看到一共读取了三次,因为在 test.txt 文件中有两个 |

3.1.4 ReadLine() 换行读取读取一行

ReadLine() (line []byte, isPrefix bool, err error)

  1. 编写 test.txt 文件

    内容为:

image-20210621221737573

  1. 代码

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    )
    
    func main() {
    	// 打开文件
    	filePath := "test.txt"
    	file, err := os.Open(filePath)
    	if err != nil {
    		return
    	}
    
    	// 关闭文件
    	defer file.Close()
     
    // 将文件读取到 NewReader 缓存中,并返回其缓冲区具有默认大小的新读取器,将文件内容写入 reader。
    	reader := bufio.NewReader(file)
    
        // 有三个返回值,一般忽略第二个
    	n, _, err := reader.ReadLine()
    	fmt.Println(n, string(n), err)
    }
    

    执行

    [22:29:03 root@go testreadr]#go run main.go 
    [49 50 51 52 53 124 54 55 56 124 57 120 120 120 120 120] 12345|678|9xxxxx <nil>
    
    # 12345|678|9xxxxx 将第一行读取

3.1.5 ReadSlice() 读取到固定字节结束

ReadBytes() 类似

  1. 编写 test.txt 文件

    内容为:

image-20210621221737573

代码:

package main

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

func main() {
	// 打开文件
	filePath := "test.txt"
	file, err := os.Open(filePath)
	if err != nil {
		return
	}

	// 关闭文件
	defer file.Close()

	// 将文件读取到 NewReader 缓存中,并返回其缓冲区具有默认大小的新读取器,将文件内容写入到 reader。
	reader := bufio.NewReader(file)
	
    // | 为分隔符
	n, err := reader.ReadSlice('|')
	fmt.Println(n, string(n), err)
}

执行

[22:40:25 root@go testreadr]#go run main.go 
[49 50 51 52 53 124] 12345| <nil>

# 12345|

通过输出发现和 ReadBytes() 类似

3.1.6 ReadString() 和 ReadBytes() 类似,返回 string

代码

package main

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

func main() {
	// 打开文件
	filePath := "test.txt"
	file, err := os.Open(filePath)
	if err != nil {
		return
	}

	// 关闭文件
	defer file.Close()

	// 将文件读取到 NewReader 缓存中,并返回其缓冲区具有默认大小的新读取器,将文件内容写入到 reader。
	reader := bufio.NewReader(file)

    // | 为分隔符
	n, err := reader.ReadString('|')
	fmt.Println(n, err)
}

执行

[22:40:26 root@go testreadr]#go run main.go 
12345| <nil>

3.1.7 WriteTo() 将输入输出

package main

import (
	"bufio"
	"os"
)

func main() {
	// 打开文件
	filePath := "test.txt"
	file, err := os.Open(filePath)
	if err != nil {
		return
	}

	// 关闭文件
	defer file.Close()

	// 将文件读取到 NewReader 缓存中,并返回其缓冲区具有默认大小的新读取器,将文件内容写入到 reader。
	reader := bufio.NewReader(file)

	// 使用 WriteTo() 然后调用 os.Stdout 终端输出 test.txt 文件类容
	reader.WriteTo(os.Stdout)
}

执行

[22:46:44 root@go testreadr]#go run main.go 
12345|678|9xxxxx
abc
123xyz
xyz

3.2 带缓存的输入

带缓冲的 io 输入,可以解决必须输入一个正确的字符串问题

image-20210621214121112

package main

import (
	"bufio"
	"os"
)

func main() {

	// 调用 bufio.NewScanner() 然后在里面调用 os.Stdin 标准输入
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Scan()
}

执行

[21:39:36 root@go testbufio]#go run main.go 
213

一次性输入多个带缓冲数据

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 通过 for 循环调用  scanner.Scan() 一次性输入多个带缓冲的值
	for scanner.Scan() {
	
		// scanner.Text() 将所有输入的值转为 string 类型输出
		fmt.Println(scanner.Text())
	}
}

执行

[21:47:52 root@go testbufio]#go run main.go 
12312
12312
asdasd
asdasd
zxczx
zxczx
^Csignal: interrupt

自己手动编写一个 带缓冲输入的函数

package main

import (
	"bufio"
	"os"
	"strconv"
)

// 读取一行转换一行
func ScanInt() (int, error) {
	scanner := bufio.NewScanner(os.Stdin)
	if scanner.Scan() {
		// strconv.Atoi(s string) (int, error) 将 string 转换为 int 返回
		// (*bufio.Scanner).Text() string 返回的是 string
		return strconv.Atoi(scanner.Text())
	}
	return 0, scanner.Err()
}

func main() {
	ScanInt()
}

3.3 带缓冲的写

image-20210621225555737

3.3.1 WriteString() 写入 string 类型

package main

import (
	"bufio"
	"os"
)

func main() {
	// 创建 test.txt
	file, err := os.Create("test.txt")
	if err != nil {
		return
	}
	defer file.Close()

	// 传递 file 到 bufio.NewWriter()
	writer := bufio.NewWriter(file)

	// 写入 string 类型 123123123
	writer.WriteString("123123123")

	// 刷新缓存,如果不刷新缓存该文件只能被创建则没有内容写入,因为缓存还在应用程序层还在内存中
	writer.Flush()
}

执行

[23:01:09 root@go testwriter]#go run main.go

已经写入

image-20210621230323829

4 文件目录操作

目录是读取该目录下的文件。

这时候就有一个问题,我们如何判断是文件夹还是目录,如果是 dir 我们如何读取目录下的文件名。

4.1 获取文件信息

image-20210622094639889

目录结构

os.File.Stat() 我们像判断一些文件的信息就可以使用该函数

func (*os.File).Stat() (fs.FileInfo, error)

package main

import (
	"fmt"
	"os"
)

func main() {
	// 不管是 file 还是 dir 我们在打开的时候都是用 open
	// 打开文件和打开文件夹都是使用 os.Open
	path := "testdir"
	file, err := os.Open(path)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()

	// 返回 fileInfo 和一个 err
	fileInfo, err := file.Stat()
	fmt.Println(fileInfo, err)

	fmt.Println(fileInfo.Name())    // 输出文件名称
	fmt.Println(fileInfo.Size())    //	输出文件大小
	fmt.Println(fileInfo.IsDir())   // 判断是否为文件夹
	fmt.Println(fileInfo.ModTime()) // 文件最后的修改时间
	fmt.Println(fileInfo.Mode())    // 文件权限属性
}

执行:

[09:50:40 root@go file]#go run main.go 
&{testdir 6 2147484141 {401043485 63759921874 0x55e540} {2050 100729273 2 16877 0 0 0 0 6 4096 0 {1624325074 403043485} {1624325074 401043485} {1624325074 401043485} [0 0 0]}} <nil>
testdir									# 输出文件名称
6										# 输出文件大小
true									# 判断是否为文件夹
2021-06-22 09:24:34.401043485 +0800 CST	# 文件最后的修改时间
drwxr-xr-x								# 文件权限属性

4.2 读取目录信息

func (f *File) Readdirnames(n int) (names []string, err error)

如果想要查看所有信息只需要将 Readdirnames(-1) 写个 -1 即可

1.编写程序

package main

import (
	"fmt"
	"os"
)

func main() {
	// 不管是 file 还是 dir 我们在打开的时候都是用 open
	// 打开文件和打开文件夹都是使用 os.Open
	path := "testdir"
	file, err := os.Open(path)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()
	
    // -1:表示输出所有的 testdir 目录下的信息
	fmt.Println(file.Readdirnames(-1))
}

2.执行

[09:55:01 root@go file]#go run main.go 
[] <nil>

# 由于当前 testdir 目录下没有任何数据所以返回的是空

3.现在往 testdir 目录中创建数据

[09:55:03 root@go file]#touch testdir/11.txt
[09:57:41 root@go file]#mkdir testdir/temp

4.再次执行该程序,我们可以看到输出的内容就是刚才我们创建的11.txt 文件和 temp 目录,但是不会显示 testdir 子目录下的内容

[09:57:56 root@go file]#go run main.go 
[11.txt temp] <nil>

4.3 获取指定目录信息

func (*os.File).ReadDir(n int) ([]fs.DirEntry, error)

我们通过遍历 []fs.DirEntry 切片,我们可以获取到该目录下的数据信息

1.编写代码

package main

import (
	"fmt"
	"os"
)

func main() {
	// 不管是 file 还是 dir 我们在打开的时候都是用 open
	// 打开文件和打开文件夹都是使用 os.Open
	path := "testdir"
	file, err := os.Open(path)
	if err != nil {
		fmt.Println(err)
		return
	}

	defer file.Close()

	// -1:表示输出所有的 testdir 目录下的信息
	fileInfos, err := file.ReadDir(-1)
	for _, fileInfo := range fileInfos {

		// fileInfo.Name() 获取指定目录下所有文件
		// fileInfo.IsDir() 判断他们是否都为 目录
		fmt.Println(fileInfo.Name(), fileInfo.IsDir())
	}
}

执行:

[10:05:56 root@go file]#go run main.go 
11.txt false	# 是文件所以为 false
temp true		# 是目录

4.4 对文件的常规操作

有的时候我们需要对文件进行拷贝或者说进行删除。

// 文件
创建 => os.Create
读取 => os.Open
获取属性 => os.Open() Stat 获取 / os.Stat
修改属性:权限、所属人 os.Chmod(修改文件权限) os.Chown(修改文件所属者)

重命名:	os.Rename() // 重命名
删除文件

// 目录
创建
读取 => os.Open
获取属性 =>  os.Open() Stat 获取 / os.Stat
修改属性
os.Chmod() // 修改文件权限
os.Chown() // 修改文件所属者
删除文件夹

4.4.1 文件/文件夹移动

image-20210622105113522

func os.Rename(oldpath string, newpath string) error

package main

import "os"

func main() {
	filename := "a.txt"
	// 把 a.txt 重命名为 b.txt
	os.Rename(filename, "b.txt")

	dirname := "d"
	// 把 d 目录重命名为 e
	os.Rename(dirname, "e")
}

执行

[10:49:21 root@go fileop]#go run main.go

执行之后重命名

image-20210622105129576

1.从当前目录移动到 temp 目录下,创建 temp 目录

[11:14:21 root@go fileop]#ls
a  b.txt  main.go  temp

# 我们将 a b.txt 移动到 temp 目录下

2.代码

package main

import "os"

func main() {
    // 把 a 和 b.txt 移动到 temp 目录下
	os.Rename("a", "temp/a")
	os.Rename("b.txt", "temp/b.txt")
}

3.执行

[11:18:41 root@go fileop]#go run main.go 
[11:19:10 root@go fileop]#ll temp/
total 0
drwxr-xr-x 2 root root 6 Jun 22 11:17 a
-rw-r--r-- 1 root root 0 Jun 22 11:17 b.txt

# 执行之后 a 和 b.txt 已经移动到了 temp 下

4.4.2 删除文件

不仅能够移除文件还可以移除目录

func os.Remove(name string) error

image-20210622105319561

package main

import "os"

func main() {
	// 删除 b.txt
	os.Remove("b.txt")

	// 删除 e
	os.Remove("e")
}

执行

[10:51:17 root@go fileop]#go run main.go

执行之后被删

image-20210622105331493

删除所有文件,我们看到 a 目录下还有 b、c、d

image-20210622111322325

[11:11:24 root@go fileop]#tree 
.
├── a
│   └── b
│       └── c
│           └── d
└── main.go

编写代码

package main

import "os"

func main() {
    // 删除 a 目录
	os.RemoveAll("a")
}

执行

[11:13:43 root@go fileop]#go run main.go 
[11:13:45 root@go fileop]#tree 
.
└── main.go
# 已经删除只剩一个 mian.go 文件

4.4.3 创建目录

func os.Mkdir(name string, perm fs.FileMode) error

package main

import "os"

func main() {
    // 创建 test 目录权限为 os.ModePerm
	os.Mkdir("test", os.ModePerm)
}

执行

[11:07:03 root@go fileop]#go run main.go 
[11:07:13 root@go fileop]#ll
total 4
-rw-r--r-- 1 root root 74 Jun 22 11:07 main.go
drwxr-xr-x 2 root root  6 Jun 22 11:07 test		# 目录创建

但是当我创建目录的时候,如果说父目录不存在我们该怎么做呢

func os.MkdirAll(path string, perm fs.FileMode) error

package main

import "os"

func main() {
    // a/b/c/d 同时创建 a、b、c、d 这 4 个目录
	os.MkdirAll("a/b/c/d", os.ModePerm)
}

执行

[11:07:25 root@go fileop]#go run main.go 
[11:11:24 root@go fileop]#tree 	
.
├── a
│   └── b
│       └── c
│           └── d
└── main.go
# 通过 tree 命令查看已经创建

5 io工具&文件路径

在 go 里面有一些工具是对 io 相关的操作。

5.1 ioutil 包

iotuil 包有一些几个函数

[14:24:41 root@go utils]#go doc ioutil
package ioutil // import "io/ioutil"

var Discard io.Writer = io.Discard
func NopCloser(r io.Reader) io.ReadCloser
func ReadAll(r io.Reader) ([]byte, error)
func ReadDir(dirname string) ([]fs.FileInfo, error)
func ReadFile(filename string) ([]byte, error)
func TempDir(dir, pattern string) (name string, err error)
func TempFile(dir, pattern string) (f *os.File, err error)
func WriteFile(filename string, data []byte, perm fs.FileMode) error

5.1.1 ReadAll() 读取文件中全部内容

func ioutil.ReadAll(r io.Reader) ([]byte, error)

  1. 创建一个文件 test.txt ,文件内容如下

    image-20210622143104385

  1. 编写程序

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"os"
    )
    
    func main() {
    	// 打开文件
    	filePath := "test.txt"
    	file, err := os.Open(filePath)
    	if err != nil {
    		return
    	}
    	// 关闭文件
    	defer file.Close()
    	 
        // 通过 ioutil.ReadAll() 读取 file 中所有的内容
    	str, err := ioutil.ReadAll(file)
    	if err == nil {
    		fmt.Println(string(str))
    	}
    }
    

    执行

    [14:30:21 root@go utils]#go run main.go 
    123123
    aaa
    中文
    啊实打实的
    
    # 通过执行我们可以发现已经读取了所有的内容

5.1.2 ReadFile() 返回文件所有内容

func ioutil.ReadFile(filename string) ([]byte, error)

ReadFile读取按文件名命名的文件并返回内容。成功的调用返回err==nil,而不是err==EOF。因为ReadFile读取整个文件,所以它不会将来自Read的EOF视为要报告的错误。

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
    
    // 定义文件路径,调用 ioutil.ReadFile() 传入文件路径
	filePath := "test.txt"
	str, err := ioutil.ReadFile(filePath)
	if err == nil {
		fmt.Println(string(str))
	}
}

执行

[15:03:47 root@go utils]#go run main.go 
123123
aaa
中文
啊实打实的

5.1.3 ReadDir() 返回所有文件夹的内容

func ioutil.ReadDir(dirname string) ([]fs.FileInfo, error)

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	dir := "/data"
	fmt.Println(ioutil.ReadDir(dir))
}

执行

[15:09:14 root@go utils]#go run main.go 
[0xc00006ea90 0xc00006e9c0 0xc00006edd0 0xc00006e8f0 0xc00006ec30 0xc00006ed00 0xc00006eb60] <nil>

5.1.4 WriteFile() 写入数据到文件中

func ioutil.WriteFile(filename string, data []byte, perm fs.FileMode) error

WriteFile 将数据写入按文件名命名的文件。

如果文件不存在,WriteFile 将使用 perm 权限创建它

(在umask之前);否则WriteFile会在写入之前将其截断,而不更改权限。

package main

import (
	"io/ioutil"
)

func main() {
 // 如果没有 a.txt 就自动创建,内容为 []byte("这是一个测试文件") ,权限0666
	ioutil.WriteFile("a.txt", []byte("这是一个测试文件"), 0666)
}

执行

[15:19:33 root@go utils]#go run main.go

image-20210622152050087

5.2 对文件路径进行处理

在 go 中有一个 path 包和一个 filepath

path 包中的东西包含在 filepath 里面

[15:39:14 root@go filepath]#go doc path
package path // import "path"

Package path implements utility routines for manipulating slash-separated
paths.

The path package should only be used for paths separated by forward slashes,
such as the paths in URLs. This package does not deal with Windows paths
with drive letters or backslashes; to manipulate operating system paths, use
the path/filepath package.

var ErrBadPattern = errors.New("syntax error in pattern")
func Base(path string) string		# 获取文件得名字
func Clean(path string) string
func Dir(path string) string		# 获取父目录得名字
func Ext(path string) string		# 获取文件后缀	
func IsAbs(path string) bool		# 判断是否为绝对路径
func Join(elem ...string) string	# 多个路径连接起来用 join
func Match(pattern, name string) (matched bool, err error)
# 文件路径匹配模式

func Split(path string) (dir, file string) # 判断父目录和文件分开

5.2.1 filepath 包

[15:48:54 root@go filepath]#go doc filepath
package filepath // import "path/filepath"

Package filepath implements utility routines for manipulating filename paths
in a way compatible with the target operating system-defined file paths.

The filepath package uses either forward slashes or backslashes, depending
on the operating system. To process paths such as URLs that always use
forward slashes regardless of the operating system, see the path package.

const Separator = os.PathSeparator ...
var ErrBadPattern = errors.New("syntax error in pattern")
var SkipDir error = fs.SkipDir
func Abs(path string) (string, error) # 获取绝对路径
func Base(path string) string		  # 获取文件名称
func Clean(path string) string		  # 清除文件中得符号
func Dir(path string) string          # 获取父目录
func EvalSymlinks(path string) (string, error)
func Ext(path string) string		  # 用来获取后缀
func FromSlash(path string) string
func Glob(pattern string) (matches []string, err error)
func HasPrefix(p, prefix string) bool # 判断文件是不是以什么为前缀得
func IsAbs(path string) bool		  # 用来判断绝对路径
func Join(elem ...string) string	  # 多个路径连接起来用 join
func Match(pattern, name string) (matched bool, err error)
func Rel(basepath, targpath string) (string, error)
func Split(path string) (dir, file string)
func SplitList(path string) []string
func ToSlash(path string) string
func VolumeName(path string) string
func Walk(root string, fn WalkFunc) error # 
func WalkDir(root string, fn fs.WalkDirFunc) error
type WalkFunc func(path string, info fs.FileInfo, err error) error

5.2.1.1 filepath.Abs() 获取文件绝对路径

func Abs(path string) (string, error)获取绝对路径

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
    
    // 获取 main.go 这个文件的绝对路径
	file, err := filepath.Abs("main.go")
	if err == nil {
		fmt.Println(file)
	}
}

执行

[16:19:04 root@go filepath]#go run main.go 
/data/go/day5/codes/filepath/main.go

5.2.1.2 filepath.Base() 获取自身文件名

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
    // 获取当前文件的 main.go 名称
	fmt.Println(filepath.Base("main.go"))
}

执行

[16:21:58 root@go filepath]#go run main.go 
main.go

5.2.1.3 filepath.Dir() 获取父目录

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
    // 获取绝对路径
	file, _ := filepath.Abs("main.go")

    // 然后再获取他的父目录
	fmt.Println(filepath.Dir(file))
}

执行

[16:24:15 root@go filepath]#go run main.go 
/data/go/day5/codes/filepath

5.2.1.4 filepath.Split() 分割

将文件和父目录进行分割

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
	path, _ := filepath.Abs("main.go")

	// 进行分割操作
	fmt.Println(filepath.Split(path))

}

执行

[16:27:32 root@go filepath]#go run main.go 
/data/go/day5/codes/filepath/ main.go

5.2.1.5 filepath.Ext() 返回文件后缀

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
	path, _ := filepath.Abs("main.go")

	fmt.Println(filepath.Ext(path))

}

执行

[16:32:49 root@go filepath]#go run main.go 
.go

5.2.1.6 filepath.HasPrefix() 以什么为前缀

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
	path, _ := filepath.Abs("main.go")

	// 判断前缀是否为 /data 目录下
	fmt.Println(filepath.HasPrefix(path, "/data"))
}

执行

[16:37:04 root@go filepath]#go run main.go 
true

5.2.1.7 filepath.IsAbs() 判断是否为绝对路径

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
	path, _ := filepath.Abs("main.go")

	fmt.Println(filepath.IsAbs(path))
}

执行

[16:37:50 root@go filepath]#go run main.go 
true

5.2.1.8 filepath.Glob() 用于文件匹配

这里我创建了以下几个文件

image-20210622164527223

这种方式类似于正则表达式

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
	fmt.Println(filepath.Glob("dir/*.go"))
	fmt.Println(filepath.Glob("dir/*.c"))
	fmt.Println(filepath.Glob("dir/*.txt"))
}

执行

[16:49:48 root@go filepath]#go run main.go 
[dir/a.go] <nil>
[dir/c.c] <nil>
[dir/b.txt] <nil>

# 我们可以看到已经匹配到了这几个文件

5.2.1.9 filepath.Walk() 将所有目录文件全部输出

package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func main() {
    // 拿到 dir 目录全路径
	path, err := filepath.Abs("dir")
	if err != nil {
		return
	}

    // 通过 filepath.Walk() 回调函数
	filepath.Walk("dir", func(root string, info os.FileInfo, err error) error {
		fmt.Println(path, info.Name())
		return nil
	})
}

执行

[17:12:03 root@go filepath]#go run main.go 
dir dir
dir a.go
dir b.txt
dir c.c

# 已经将 dir 目录下所有的文件全部输出

5.2.1.10filepath.Join() 用来连接多个路径

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
	// 将 /root 和 /data 进行链接
	fmt.Println(filepath.Join("/root", "/data"))
}

执行

[17:12:27 root@go filepath]#go run main.go 
/root/data

# 他就会把路径拼接起来

6 文件格式(编解码)

所有的编解码过程都是需要第一步先注册,编解码的过程如下:

1.注册,把需要存储的对象进行注册
2.打开文件
3.创建 编码或者解码的对象
4.进行编码或者是解码

注意:

编码:是将内存中的数据写到文件中的一个过程

解码:是将文件中的数据写到内存中,这就是一个解码的过程

表示存储到文件中数据格式的一个说明,这个格式可以任意自定义

#id , name , password , tel , addr

name , password , tel , addr 中不可能存在的一个字符作为分割符,

# 每一行是一个用户数据
1,zz,123456,177,重庆
2,gg,789101,188,成都

// users []User 这个数组保存到文件中(编码过程)
// 使用 fmt.Fprintf()

// 文件中的内容如何到切片里面去	(解码过程)
    读取一行,我们使用逗号分割
// 使用 strings.Split(",",-1)
	转换格式 => User => append

6.1 编码(持久化的过程)

json(文本),csv(文本),gob(二进制,go 特有的不能跨语言)

一般存储的数据都需要注册一下

image-20210623143855505

6.1.1 gob 持久化范例

6.1.1.1 gob 持久化写入范例(编码)

所谓编码就是将内存中的数据写到文件中的一个过程

func gob.Register(value interface{})注册使用

func gob.NewEncoder(w io.Writer) *gob.Encoder 以写的方式打开

func (*gob.Encoder).Encode(e interface{}) error 进行编码

package main

import (
	"encoding/gob"
	"fmt"
	"os"
)

type User struct {
	ID   int
	Name string
}

func main() {
	// user := []User{
	// 	User{1, "zz"},
	// 	User{2, "gg"},
	// }

	// 也可以使用下面的方式简写,给 User 结构体赋初始值
	users := []User{
		{1, "zz"},
		{2, "gg"},
	}

	// 注册,将 User{} 注册进去
	gob.Register(User{})

	// 编码
	file, err := os.Create("user.gob")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	// 导入 gob 包
	encoder := gob.NewEncoder(file)

	// 把 users 变量传入进行编码
	// 返回错误,如果没有返回错误就会把持久化的东西存储到文件中
	fmt.Println(encoder.Encode(users))
}

执行

[14:37:29 root@go fileformat]#go run main.go 
<nil>

由于 gob 文件类型是一个二进制文件

image-20210623144846487

6.1.1.2 gob 读取范例(解码)

所谓解码其实就是将文件中的内容写到内存中,这就是一个解码的过程

解码的时候注意解码类型和文件原类型和字段属性一定要一样

在解码的时候也是需要打开文件进行解码

func gob.NewDecoder(r io.Reader) 读取文件

func (*gob.Decoder).Decode(e interface{}) error 返回 error

package main

import (
	"encoding/gob"
	"fmt"
	"os"
)

type User struct {
	ID   int
	Name string
}

func main() {
	// 注册,将 User{} 注册进去
	gob.Register(User{})

	// 解码
	// 对 user.gob 文件进行解码,打开 user.gob 文件
	file, err := os.Open("user.gob")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	// 导入 gob 包,并且读取 file 变量中的 user.gob 文件
	decoder := gob.NewDecoder(file)

	// 解码肯定需要放到某一个位置里面去
	// 所以这里定义的就是 users 变量类型为 []User
	// 因为 user.gob 文件就是一个 []User 类型的二进制文件
	var users []User
	err1 := decoder.Decode(&users)
	if err == nil {
		fmt.Println(users)
	} else {
		fmt.Println(err1)
	}
}

执行

[14:44:28 root@go fileformat]#go run main.go 
[{1 zz} {2 gg}]

6.1.2 CSV 格式

CSV 的文件后缀一般是 .csv ,一行表示一行数据,其实就是一个表格文本文件。

image-20210623160457065

[16:09:49 root@go fileformat]#go doc csv
package csv // import "encoding/csv"

type ParseError struct{ ... }
type Reader struct{ ... }
    func NewReader(r io.Reader) *Reader	# 用来读
type Writer struct{ ... }
    func NewWriter(w io.Writer) *Writer	# 用来写

6.1.2.1 CSV 格式编码

我们进行编码的时候就是写入文件操作

[16:09:57 root@go fileformat]#go doc csv.writer
package csv // import "encoding/csv"



func NewWriter(w io.Writer) *Writer	# 传递需要写入的文件
func (w *Writer) Error() error
func (w *Writer) Flush()	# 写完之后刷新,将其写入到文件中
func (w *Writer) Write(record []string) error # 每次写一行
func (w *Writer) WriteAll(records [][]string) error # 写入多行
package main

import (
	"encoding/csv"
	"os"
	"strconv"
)

type User struct {
	Id   int
	Name string
}

func main() {
	users := []User{
		{1, "zz"},
		{2, "gg"},
	}

	file, _ := os.Create("user.csv")
	writer := csv.NewWriter(file)

	// Write() 是每行每行的写入,所以需要通过 for 循环遍历 users 的值
	for _, user := range users {
		// 由于 usre.Id 是 int 类型,这里需要通过 strconv.Itoa 返回 string 类型
		writer.Write([]string{strconv.Itoa(user.Id), user.Name})
	}

	writer.Flush()
}

执行

[16:18:54 root@go fileformat]#go run csv.go

打开该文件,就会得到一个表格文件

image-20210623165047227

6.1.2.2 CVS 格式解码

[16:56:42 root@go fileformat]#go doc csv.reader

func NewReader(r io.Reader) *Reader
func (r *Reader) Read() (record []string, err error) #一次读取一行
func (r *Reader) ReadAll() (records [][]string, err error) # 一次读取所有

范例

package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"strconv"
)

type User struct {
	Id   int
	Name string
}

func main() {
	// 解码需要定义一个和编码时候一样的类型,这里为 users
	users := []User{}

	// 然后对我们需要解码的文件进行打开操作
	file, _ := os.Open("user.csv")

	defer file.Close()

	// csv.NewReader() 方法读取 file
	reader := csv.NewReader(file)

	// 通过 for{} 进行每行遍历。直到 err != nil 就表示文件读取完毕
	for {
		line, err := reader.Read()
		if err != nil {
			if err != io.EOF {
				fmt.Println(err)
			}
			break
		}

		// 由于 user.id 是一个 int 类型,通过 strconv.Atoi 将 string 转为 int
		id, _ := strconv.Atoi(line[0])
		// 追加到 users 切片结构体中
		users = append(users, User{id, line[1]})
	}
	fmt.Println(users)
}

执行

将 user.csv 文件内容就行解码

[17:18:33 root@go fileformat]#go run csvread.go 
[{1 zz} {2 gg}]

7 作业

7.1 复制文件(只是文件)

  1. 复制文件(只是文件)

    cp 命令 -s srcFile -d dstFile

    srcFile 是文件的时候进行复制

    dstFile 文件存在提示用户是否覆盖,y => 覆盖

    package main
    
    import (
    	"bufio"
    	"flag"
    	"fmt"
    	"io/ioutil"
    	"os"
    )
    
    func Copy() {
    	var src string
    	var dst string
    	var yes bool
    	var help bool
    
    	flag.StringVar(&src, "s", "", "Please input the copy target file!	")
    	flag.StringVar(&dst, "d", "", "Please input the copy target file!	")
    	flag.BoolVar(&yes, "y", false, "Whether to cover (y means to cover)!")
    	flag.BoolVar(&help, "h", false, "This is a help:")
    
    	flag.Usage = func() {
    		fmt.Println("-s srcfile -d dstfile")
    		flag.PrintDefaults()
    	}
    
    	flag.Parse()
    
    	if help {
    		flag.Usage()
    		return
    	}
    
    	srcfile, err := os.Open(src)
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    
    	defer srcfile.Close()
    
    	readStr, err := ioutil.ReadAll(srcfile)
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    
    	_, err = os.Stat(dst)
    	if err != nil {
    		dstfile, _ := os.OpenFile(dst, os.O_CREATE|os.O_RDWR, 0666)
    		writer := bufio.NewWriter(dstfile)
    		writer.WriteString(string(readStr))
    		writer.Flush()
    	} else {
    		cover := ""
    		fmt.Println("覆盖请输入(y):")
    		fmt.Scanln(&cover)
    		if cover == "y" {
    			fmt.Println("覆盖中")
    			dfile, _ := os.OpenFile(dst, os.O_TRUNC|os.O_RDWR, 0666)
    			writer := bufio.NewWriter(dfile)
    			writer.WriteString(string(readStr))
    			writer.Flush()
    		}
    	}
    
    }
    
    func main() {
    	Copy()
    }
    

    执行

    image-20210622190146899

    image-20210622190126682

如果 dst 存在就提示是否覆盖

7.2 作业 2

  1. 给一个路径,这个路径可能是文件可能是目录

    如果是目录打印出所有的文件(包含子目录)

package main

import (
	"fmt"
	"io/ioutil"
)

func GetDir(dir string) {
	dirInfo, _ := ioutil.ReadDir(dir)
	for _, file := range dirInfo {
        // 如果是 目录 就函数递归
		if file.IsDir() {
			GetDir(dir + "/" + file.Name())
		} else {
            // 否则直接打印当前文件名
			fmt.Println(dir + "/" + file.Name())
		}
	}
}

func main() {
	dir := "/"
	GetDir(dir)
}

执行

[15:13:06 root@go day5]#go run main.go 
//data/go/pkg/mod/golang.org/x/image@v0.0.0-20210220032944-ac19c3e999fb/example/font/main.go
//data/go/pkg/mod/golang.org/x/image@v0.0.0-20210220032944-ac19c3e999fb/font/basicfont/basicfont.go
//data/go/pkg/mod/golang.org/x/image@v0.0.0-20210220032944-ac19c3e999fb/font/basicfont/basicfont_test.go
//data/go/pkg/mod/golang.org/x/image@v0.0.0-20210220032944-ac19c3e999fb/font/basicfont/data.go
//data/go/pkg/mod/golang.org/x/image@v0.0.0-20210220032944-ac19c3e999fb/font/basicfont/gen.go^Csignal: interrupt
.....

7.3 作业3

  1. 基于作业 2 ,只打印 .go 后缀, .c 后缀的文件

package main

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
)

func Get(dir string) {

	dirInfo, _ := ioutil.ReadDir(dir)

	GoSuffix := []string{}
	CSuffix := []string{}

	for _, file := range dirInfo {
		if file.IsDir() {
			Get(dir + "/" + file.Name())
		} else {
            // 如果不是目录判断文件后缀是否是 .go 和 .c 结尾,如果是复制给 GoSuffix 和 CSuffix 
			GoSuffix, _ = filepath.Glob(dir + "/*.go")
			CSuffix, _ = filepath.Glob(dir + "/*.c")
		}
	}

    // 遍历输出
	for _, file := range GoSuffix {
		fmt.Println(file)
	}

	for _, file := range CSuffix {
		fmt.Println(file)
	}
}

func main() {
	dir := "/data/go/exec/"
	Get(dir)
}

执行

[18:19:10 root@go csv]#go run decode.go 
/data/go/exec/day5/csv/decode.go
/data/go/exec/day5/csv/encode.go
/data/go/exec/day5/Encode.go
/data/go/exec/day5/uncode.go
/data/go/exec/day5/test.c
/data/go/exec/day5/txt.c
....

# 通过输出将 .go 和 .c 的文件输出

7.4 作业4

  1. 基于作业 3 ,统计代码行数

7.5 作业5

用户管理,持久化到文件中,

  1. 每次操作的时候如果发生了修改就进行保存

  2. 加载文件,程序启动的时候

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇