• Go语言 文件操作


    Go语言

    11. 文件操作

    文件通常被分为两类:文本文件和二进制文件。所有你能用记事本打开并正常显示的文件都可以叫作文本文件,而像图片、可执行程序、压缩包等文件叫作二进制文件。

    11.1 目录基本操作

    目录是存放文件的地方,为树形层次结构。

    Windows文件系统的特点是分盘,每个盘符有自己的根目录,形成多个树形并排结构,也就是我们常说的C盘、D盘等。

    Windows的目录不区分大小写,也就是说,“C:\windows”和“C:\WINDOWS”表示的是同一个目录。

    11.1.1 列目录

    计算机的文件处理主要会用到Go语言提供的I/O操作库,为了方便开发者使用,Go语言将I/O操作封装在了如下几个包中:

    • io——为IO原语(I/O primitives)提供基本的接口。
    • io/ioutil——封装一些实用的I/O函数。
    • fmt——实现格式化I/O,类似于C语言中的printf和scanf。
    • bufio——实现带缓冲I/O。

    最常用的I/O操作库是“fmt”,即控制台的输入输出,基本每个程序都离不开这个库。

    想要读取一个目录的内容,可以使用io/ioutil库中的ReadDir方法,此方法返回一个有序列表,列表内容是dirname指定的目录的目录信息。

    package main
    
    import (
       "fmt"
       "io/ioutil"
    )
    
    func main() {
       dir, err := ioutil.ReadDir("c:\\users")
       if err != nil {
          fmt.Println(err)
       }
       for _, file := range dir {
          fmt.Println(file.Name())
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    通过os.FileInfo的IsDir属性就可以判断出该文件是否是一个文件夹。可以将列目录这个操作封装成一个函数。

    package main
    
    import (
       "fmt"
       "io/ioutil"
    )
    
    func ListDir(dirPth string) error {
       dir, err := ioutil.ReadDir(dirPth)
       if err != nil {
          return err
       }
    
       for _, fi := range dir {
          if fi.IsDir() { //忽略目录
             fmt.Println("目录:" + fi.Name())
          } else {
             fmt.Println("文件" + fi.Name())
          }
       }
       return nil
    }
    
    func main() {
       ListDir("C:\\users")
    
    }
    
    • 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

    在这里插入图片描述

    递归遍历一个文件夹内的所有文件。Go标准库提供了一个更加方便的操作函数,这个函数位于path/filepath库中。

    func Walk(root string, walkFn WalkFunc) error
    
    • 1

    Walk函数会遍历root指定的目录下的文件树,对每一个该文件树中的目录和文件都会调用walkFn,包括root自身。所有访问文件/目录时遇到的错误都会传递给walkFn过滤。文件是按词法顺序遍历的,这让输出显得更漂亮,但也导致处理非常大的目录时效率会降低。Walk函数不会遍历文件树中的符号链接(快捷方式)文件包含的路径。

    package main
    
    import (
       "fmt"
       "os"
       "path/filepath"
    )
    
    func WalkDir(path string) {
       err := filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
          if f == nil {
             return err
          }
          if f.IsDir() {
             return nil
          }
          println(path)
          return nil
       })
       if err != nil {
          fmt.Printf("filepath.walk() returned %v\n", err)
       }
    }
    
    func main() {
       WalkDir("g:\\丁家雄自学golang")
    }
    
    • 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

    在这里插入图片描述

    11.1.2 创建目录

    Go标准库中的os库提供了平台无关性的操作系统功能接口。创建目录时可以使用os库的如下接口:

    func Mkdir(name string, perm FileMode) error
    
    • 1

    Mkdir接口函数使用指定的权限和名称创建一个目录。如果出错,会返回*PathError底层类型(即语言自带的类型)的错误。

    package main
    
    import (
       "fmt"
       "os"
    )
    
    func createDir(path string, dirName string) {
       dirPath := path + "\\" + dirName
    
       err := os.Mkdir(dirPath, 0777)
       if err != nil {
          fmt.Println(err)
       }
       os.Chmod(dirPath, 0777)
       fmt.Println("Create Dir =>" + path + dirName)
    }
    
    func main() {
       createDir("g:\\test", "test")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    当文件夹存在时,再次调用Mkdir接口函数会抛出“文件夹已存在”的错误。Mkdir一次只能创建一级目录,如果使用此函数创建多级目录则会报“The system cannot find the path specified.”的错误。这时只能使用另一个创建目录的接口函数:

    func MkdirAll(path string, perm FileMode) error
    
    • 1

    MkdirAll使用指定的权限和名称创建一个目录,包括任何必要的上级目录,并返回nil,否则返回错误。

    package main
    
    import (
       "fmt"
       "os"
    )
    
    func createDirAll(path string, dirName string) {
       driPath := path + "\\" + dirName
       fmt.Println("Create Dir =>" + dirName)
    
       err := os.MkdirAll(driPath, 0777)
       if err != nil {
          fmt.Println(err)
       } else {
          fmt.Println("Create success!")
       }
       os.Chmod(driPath, 0777)
    }
    
    func main() {
       createDirAll("g:\\test", "dir1\\dir2\\dir3")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    使用MkdirAll创建一个目录path,当path已经存在时,不会报错,直接返回nil。

    11.1.3 删除目录
    func Remove(name string) error
    
    • 1

    Remove删除name指定的文件或目录。此接口只能删除空文件夹,如果文件夹非空,则会删除失败,返回“文件夹非空”错误。

    func deleteEmptyDir(dirPath string) {
        fmt.Println("Delete Dir => " + dirPath)
        err := os.Remove(dirPath)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println("Delete Success!")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对于非空文件夹,可以使用以下接口删除:

    func RemoveAll(path string) error
    
    • 1
    func deleteNotEmptyDir(dirPath string) {
        fmt.Println("Delete Dir => " + dirPath)
        err := os.RemoveAll(dirPath)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println("Delete Success!")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    11.2 文件基本操作

    Linux下的文件权限。文件有三种权限,分别为读取、写入和执行,对应字母为r、w、x。

    Linux下权限的粒度有拥有者、所属组、其他组三种。每个文件都可以针对三种粒度,设置不同的rwx(读、写、执行)权限。通常情况下,一个文件只能归属于一个用户和组,如果其他的用户想拥有这个文件的权限,则可以将该用户加入具备权限的群组,一个用户可以同时归属于多个组。

    权限权限数值具体作用
    r4read,读取。当前用户可以读取文件内容;当前目录可以浏览目录
    w2write,写入。当前用户可以新增或修改文件内容;当前用户可以删除、
    移动目录或目录内文件
    x1execute,执行。当前用户可以执行文件;当前用户可以进入目录

    在创建一个文件或文件夹时,通常会赋予这个文件一定的权限。例如777权限就代表给这个文件的拥有者、所属组、其他用户赋予的读写执行权限。

    11.2.1 文件创建与打开

    对于文件的创建与打开,使用的是标准库os中的OpenFile。

    func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
    
    • 1

    OpenFile是一个更底层的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果操作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。

    位掩码参数flag用于指定文件的访问模式,可用的值在os中定义为常量

    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 // 如果可能,打开时清空文件
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其中,O_RDONLY、O_WRONLY、O_RDWR应该只指定一个,剩下的通过“|”操作符来指定。该函数内部会给flags加上syscall.O_CLOEXEC,在fork子进程时会关闭通过OpenFile打开的文件,即子进程不会重用该文件描述符。

    位掩码参数perm指定了文件的模式和权限位,类型是os.FileMode

    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权限位(用于通过&获取类型位)
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    以上常量在所有操作系统都有相同的含义。

    package main
    
    import (
       "fmt"
       "os"
    )
    
    func main() {
       file, err := os.OpenFile("g:\\test\\1.txt", os.O_RDWR|os.O_CREATE, 0776)
    
       if err != nil {
          fmt.Println(err)
       }
       fmt.Println(file)
       file.Close()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    “os.O_RDWR|os.O_CREATE”表示以读写方式打开文件,如果文件不存在,则创建这个文件。

    Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。

    func Open(name string) (file *File, err error)
    
    • 1

    Create采用模式0666(任何人都可读写,不可执行)创建一个名为name的文件,如果文件已存在就会截断它(为空文件)。如果成功,返回的文件对象可用于I/O;对应的文件描述符具有O_RDWR模式。如果出错,错误底层类型是*PathError。

    func Create(name string) (file *File, err error)
    
    • 1
    11.2.2 文件读取

    读取文件可以使用os库中的Read接口:

    func (f *File) Read(b []byte) (n int, err error)
    
    • 1

    Read方法从文件中读取最多len(b)字节数据并写入byte数组b,它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取0个字节且返回值err为io.EOF。

    package main
    
    import (
       "fmt"
       "os"
    )
    
    func ReadFile(path string) {
       file, err := os.Open(path)
       if err != nil {
          fmt.Println(err)
       }
       buf := make([]byte, 1024)
       fmt.Println("以下是文件内容:")
       for {
          len, _ := file.Read(buf)
          if len == 0 {
             break
          }
          fmt.Println(string(buf))
       }
       file.Close()
    }
    
    func main() {
       ReadFile("g:\\test\\1.txt")
    }
    
    • 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

    在这里插入图片描述

    当遇到特别大的文件,并且只需要读取文件最后部分的内容时,Read接口就不能满足我们的需要了,这时可以使用另外一个文件读取接口ReadAt,这个接口可以指定从文件的什么位置开始读取。

    func (f *File) ReadAt(b []byte, off int64) (n int, err error)
    
    • 1

    ReadAt从指定的位置(相对于文件开始位置)读取len(b)字节数据并写入byte数组b。它返回读取的字节数和可能遇到的任何错误。当n<len(b)时,本方法总是会返回错误;如果是因为到达文件结尾,返回值err会是io.EOF。

    package main
    
    import (
       "fmt"
       "os"
    )
    
    func ReadFile2(path string) {
       file, err := os.Open(path)
       if err != nil {
          fmt.Println(err)
       }
       buf := make([]byte, 1024)
       fmt.Println("以下是文件内容:")
       _, _ = file.ReadAt(buf, 9)
       fmt.Println(string(buf))
       _ = file.Close()
    }
    
    func main() {
       ReadFile2("g:\\test\\2.txt")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    Read和ReadAt的区别:前者从文件当前偏移量处开始读取,且会改变文件当前的偏移量;而后者从off指定的位置开始读取,且不会改变文件当前偏移量。

    11.2.3 文件写入

    与文件读取相比,向文件写入内容也有两个接口,分别为Write和WriteAt。

    Write向文件中写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值为n!=len(b),本方法会返回一个非nil的错误。

    func (f *File) Write(b []byte) (n int, err error)
    
    • 1
    package main
    
    import (
       "fmt"
       "os"
    )
    
    func main() {
       file, err := os.Create("G:\\test\\4.txt")
       if err != nil {
          fmt.Println(err)
       }
       data := "我是数据\r\n"
    
       for i := 0; i < 3; i++ {
          file.Write([]byte(data))
       }
       file.Close()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    使用WriteAt可以指定从文件的什么位置开始写:

    WriteAt在指定的位置(相对于文件开始位置)写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值为n!=len(b),本方法会返回一个非nil的错误。

    package main
    
    import (
       "fmt"
       "os"
       "strconv"
    )
    
    func main() {
       file, err := os.Create("G:\\test\\5.txt")
       if err != nil {
          fmt.Println(err)
       }
       for i := 0; i < 3; i++ {
          ix := i * 64
          file.WriteAt([]byte("我是数据"+strconv.Itoa(i)+"\r\n"), int64(ix))
       }
       file.Close()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    使用Write和WriteAt接口函数对文件进行写入数据时,会将原文件覆盖,并从文件开始位置处写入内容。

    11.2.4 删除文件

    删除文件使用os库中的Remove和RemoveAll接口,与删除目录的接口一致。

    删除文件

    err := os.Remove("C:\\Windows\\Temp\\1.txt")
    
    • 1

    删除指定path下的所有文件

    err = os.RemoveAll("C:\\Windows\\Temp\\test\\")
    
    • 1

    11.3 处理JSON文件

    JSON(JavaScript Object Notation,JS对象简谱)是一种轻量级的数据交换格式。JSON最初是属于JavaScript的一部分,后来由于其良好的可读性和便于快速编写的特性,现在已独立于语言,基本上所有的语言都支持JSON数据的编码和解码。

    JSON中的键都是字符串形式,值可以取任意类型。它有以下三种结构:

    • 值为字符串或数组类型:{“name”:“John”,“age”:20}
    • JSON数组:[{“name”:" John",“age”:20},{“name”:“Tom”,“age”:21}]
    • 值为对象类型:{“name”:" John ", “birthday”:{“month”:8,“day”:26}},类似于对象嵌套对象

    大括号“{}”用来描述一组“不同类型的无序键值对集合”,方括号“[]”用来描述一组“相同类型的有序数据集合”。

    11.3.1 编码json

    标准库提供了encoding/json库来处理JSON。编码JSON,即从其他的数据类型编码成JSON字符串。

    func Marshal(v interface{}) ([]byte, error)
    
    • 1

    Marshal函数返回interface{}类型的JSON编码,通常interface{}类型会使用map或者结构体。为了让输出的JSON字符串更加直观,可以使用另一个JSON编码接口,对输出的JSON进行格式化操作。

    func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
    
    • 1

    MarshalIndent类似于Marshal,但会使用缩进将输出格式化。

    package main
    
    import (
       "encoding/json"
       "fmt"
    )
    
    func main() {
       m := make(map[string]interface{}, 6)
       m["name"] = "小丁"
       m["age"] = 20
       m["graduated"] = false
       m["birthday"] = "1999-10-12"
    
       m["company"] = "xx学校"
       m["language"] = []string{"Go", "Java", "Python"}
    
       //编码成json
       result, _ := json.Marshal(m)
       resultFormat, _ := json.MarshalIndent(m, "", "     ")
    
       fmt.Println("result = ", string(result))
       fmt.Println("resultFormat = ", string(resultFormat))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    大多数情况下,我们会使用struct结构体来进行快速的JSON编码、解码,特别是在JSON解码时,使用struct会相当方便。

    这里Goland集成了一个非常方便的工具,直接根据json生成结构体

    在这里插入图片描述

    在这里插入图片描述

    package main
    
    import (
       "encoding/json"
       "fmt"
    )
    
    type Person struct {
       Name      string   `json:"name"`
       Age       int      `json:"age"`
       Graduated bool     `json:"graduated"`
       Birthday  string   `json:"birthday"`
       Company   string   `json:"company"`
       Language  []string `json:"language"`
    }
    
    func main() {
       person := Person{"小丁", 22, false, "1999-10-12", "xx学校", []string{"Go", "Python", "Java"}}
    
       result, err := json.Marshal(person)
       if err != nil {
          fmt.Println(err)
       }
       fmt.Println("result = ", string(result))
    }
    
    • 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

    在这里插入图片描述

    在定义struct字段的时候,可以在字段后面添加标签来控制编码/解码的过程:是否要编码或解码某个字段,JSON中的字段名称是什么。可以选择的控制字段有三种:

    • -:不要解析这个字段
    • omitempty:当字段为空(默认值)时,不要解析这个字段。比如false、0、nil、长度为0的array、map、slice、string。
    • FieldName:当解析JSON的时候,使用这个名字

    json:"name"”就是定义的第三类标签,表示将Name属性的key值解析为name。

    11.3.2 解码JSON

    解码JSON会使用到Unmarshal接口,也就是Marshal的反操作。

    func Unmarshal(data []byte, v interface{}) error
    
    • 1

    Unmarshal函数解析JSON编码的数据并将结果存入v指向的值。要将JSON数据解码写入一个接口类型值,函数会将数据解码为如下类型写入接口。

    Bool 对应JSON布尔类型
    float64 对应JSON数字类型
    string 对应JSON字符串类型
    []interface{} 对应JSON数组
    map[string]interface{} 对应JSON对象
    nil 对应JSON的null
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果一个JSON值不匹配给出的目标类型,或者一个JSON数字写入目标类型时溢出,Unmarshal函数会跳过该字段并尽量完成其余的解码操作。

    map解码json

    package main
    
    import (
       "encoding/json"
       "fmt"
    )
    
    func main() {
       jsonStr := `{
    "name": "小丁",
    "age": 22,
    "graduated": false,
    "birthday": "1999-10-12",
    "company": "xx学校",
    "language": [
    "Go",
    "Java",
    "Python"
      ]
    }`
    
       //创建map
       m := make(map[string]interface{}, 6)
       err := json.Unmarshal([]byte(jsonStr), &m)
    
       if err != nil {
          fmt.Println(err)
       }
       fmt.Println("m = ", m)
       //类型断言
       for key, value := range m {
          switch data := value.(type) {
          case string:
             fmt.Printf("map[%s]的值类型为string,value = %s\n", key, data)
          case float64:
             fmt.Printf("map[%s]的值类型为int,value = %f\n", key, data)
          case bool:
             fmt.Printf("map[%s]的值类型为bool,value = %t\n", key, data)
          case []string:
             fmt.Printf("map[%s]的值类型为[]string,value = %v\n", key, data)
          case []interface{}:
             fmt.Printf("map[%s]的值类型为[]interface{},value =%v\n", key, data)
          }
       }
    }
    
    • 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

    在这里插入图片描述

    使用map解码JSON需要对其类型进行判断,非常烦琐

    使用结构体解析sjon

    package main
    
    import (
       "encoding/json"
       "fmt"
    )
    
    type Person struct {
       Name      string   `json:"name"`
       Age       int      `json:"age"`
       Graduated bool     `json:"graduated"`
       Birthday  string   `json:"birthday"`
       Company   string   `json:"company"`
       Language  []string `json:"language"`
    }
    
    func main() {
       jsonStr := `{
    "name": "小丁",
    "age": 22,
    "graduated": false,
    "birthday": "1999-10-12",
    "company": "xx学校",
    "language": [
    "Go",
    "Java",
    "Python"
      ]
    }`
    
       var person Person
       err := json.Unmarshal([]byte(jsonStr), &person)
       if err != nil {
          fmt.Println(err)
       }
       fmt.Printf("person = %+v", person)
    }
    
    • 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

    在这里插入图片描述

    GitHub开源了一个比标准库解析速度快10倍的fastjson库(https://github.com/valyala/fastjson)

  • 相关阅读:
    kruskal重构树
    Elasticsearch部署中的两大常见问题及其解决方案
    Django面对高并发现象时处理方法
    已知CSIDL常量值,求其对应路径。
    HTML+CSS+JS电影网页设计 DW个人网页制作 Hbuilder制作简单的电影网页 在线电影网页设计与制作 web前端大作业
    D - All Assign Point Add
    光明区关于促进科技创新的若干措施(征求意见稿)
    虚实融合 智兴百业 | 赵捷副市长莅临拓世科技集团筹备展台指导,本月19号!拓世科技集团与您相约世界VR产业大会
    Dubbo Invoker
    电商前台项目(一):项目前的初始化及搭建
  • 原文地址:https://blog.csdn.net/weixin_44226181/article/details/125905504