Golang文件操作

news2024/9/23 9:30:14

文章目录

  • 文件操作
    • 基本介绍
    • 普通的文件操作方式(os包)
    • 带缓冲的文件操作方式(bufio包)
    • 文件拷贝操作(io包)
  • 命令行参数
    • 基本介绍
    • 解析命令行参数(flag包)
  • JSON
    • 基本介绍
    • JSON序列化
    • JSON反序列化

文件操作

基本介绍

基本介绍

  • 文件操作是指对计算机文件进行读取、写入、修改、删除和移动等操作的过程,它可以用于读取配置文件、存储日志、处理用户上传的文件等,Go中主要通过os和bufio包提供文件操作功能。
  • 文件在程序中是以流的形式进行操作的,我们把数据在数据源(文件)和程序(内存)之间经历的路径叫做流。其中数据从数据源到程序的路径叫做输入流,数据从程序到数据源的路径叫做输出流。

文件流示意图如下:

在这里插入图片描述

普通的文件操作方式(os包)

os包介绍

在os包中,File类型代表一个打开的文件,其封装了与文件相关的操作和属性。File结构体的定义如下:

type File struct {
	*file // os specific
}

File结构体中以*type的方式嵌套了file类型的匿名结构体指针,实际文件的属性信息都存储在file结构体中。file结构体的定义如下:

type file struct {
	pfd         poll.FD
	name        string
	dirinfo     atomic.Pointer[dirInfo] // nil unless directory being read
	nonblock    bool                    // whether we set nonblocking mode
	stdoutOrErr bool                    // whether this is stdout or stderr
	appendMode  bool                    // whether file is opened for appending
}

file结构体各字段说明:

  • pfd:用于与底层的操作系统文件描述符进行交互。
  • name:表示文件的名称(包括路径)。
  • dirinfo:用于在读取目录时缓存目录的信息(打开的文件是目录时被使用)。
  • nonblock:表示文件是否设置为非阻塞模式。
  • stdoutOrErr:表示文件是否是标准输出或标准错误。
  • appendMode:表示文件是否以追加模式打开。

每一个打开的文件都对应一个文件描述符,file结构体中的pfd是poll.FD类型的,实际文件对应的文件描述符就存储在poll.FD结构体的Sysfd字段中。FD结构体的定义如下:

type FD struct {
	// Lock sysfd and serialize access to Read and Write methods.
	fdmu fdMutex

	// System file descriptor. Immutable until Close.
	Sysfd int

	// Platform dependent state of the file descriptor.
	SysFile

	// I/O poller.
	pd pollDesc

	// Semaphore signaled when file is closed.
	csema uint32

	// Non-zero if this file has been set to blocking mode.
	isBlocking uint32

	// Whether this is a streaming descriptor, as opposed to a
	// packet-based descriptor like a UDP socket. Immutable.
	IsStream bool

	// Whether a zero byte read indicates EOF. This is false for a
	// message based socket connection.
	ZeroReadIsEOF bool

	// Whether this is a file rather than a network socket.
	isFile bool
}

打开文件

在os包中,使用OpenFile函数打开文件,该函数的函数原型如下:

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

参数说明:

  • name:表示需要打开的文件名称(包括路径)。
  • flag:表示打开文件的方式。
  • perm:表示新创建文件的权限,通常设置为0666(表示任何人都可读写,不可执行)。

返回值说明:

  • file:如果打开文件成功,将返回文件对应的File结构体。
  • err:如果打开文件过程中出错,将返回非nil的错误值。

打开文件的方式可以使用以下标注之一或它们的组合。如下:

参数选项含义
O_RDONLY以只读方式打开文件
O_WRONLY以只写方式打开文件
O_RDWR以读写方式打开文件
O_APPEND以追加的方式打开文件
O_CREATE如果文件不存在,则创建文件
O_EXCL与O_CREATE一起使用,确保创建新文件时不会覆盖现有文件
O_SYNC在每次写入操作后同步文件内容到磁盘
O_TRUNC如果文件已存在,将截断文件为零长度

打开文件示例如下:

package main

import (
	"fmt"
	"os"
)

func main() {
	name := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\NormalOperation\data.txt`
	// 打开文件
	file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file error, err = %v\n", err)
		return
	}
	fmt.Printf("open file success, fd = %v\n", file.Fd()) // open file success, fd = 420
}

说明一下:

  • 通过File结构体的Fd方法,可以获取该文件对应的文件描述符。文件的文件描述符由底层操作系统分配,每次打开文件时分配的文件描述符可能不同。

关闭文件

在os包中,使用File结构体的Close方法关闭文件,该方法的原型如下:

func (f *File) Close() error

返回值说明:

  • 关闭文件成功返回nil,否则返回非nil的错误值。

关闭文件示例如下:

package main

import (
	"fmt"
	"os"
)

func main() {
	name := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\NormalOperation\data.txt`
	// 1、打开文件
	file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file error, err = %v\n", err)
		return
	}
	// 2、延迟关闭文件
	defer file.Close()
}

说明一下:

  • 文件操作完毕后需要及时调用Close方法对文件进行关闭,避免造成文件描述符泄露。
  • 通常利用defer机制对文件进行延迟关闭,在defer语句之后仍然可以操作文件,文件将会在函数执行完毕后自动关闭。

获取文件属性信息

在os包中,使用File结构体的Stat方法获取文件的属性信息,该方法的原型如下:

func (f *File) Stat() (fi FileInfo, err error)

返回值说明:

  • fi:如果方法调用成功,将返回文件的属性信息。
  • err:如果方法调用过程中出错,将返回非nil的错误值。

Stat方法获取到的文件信息FileInfo是一个接口类型,该类型的定义如下:

type FileInfo interface {
	Name() string       // base name of the file
	Size() int64        // length in bytes for regular files; system-dependent for others
	Mode() FileMode     // file mode bits
	ModTime() time.Time // modification time
	IsDir() bool        // abbreviation for Mode().IsDir()
	Sys() any           // underlying data source (can return nil)
}

FileInfo接口中各方法说明:

  • Name方法:返回文件的基本名称(不包含路径)。
  • Size方法:返回文件的大小。
  • Mode方法:返回文件的权限和模式位信息。
  • ModTime方法:返回文件最后一次修改时间。
  • IsDir方法:判断文件是否是一个目录。
  • Sys方法:返回底层数据源,通常是操作系统特定的文件信息(可能返回nil)。

获取文件属性信息示例如下:

package main

import (
	"fmt"
	"os"
)

func main() {
	name := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\NormalOperation\data.txt`
	// 1、打开文件
	file, err := os.OpenFile(name, os.O_RDONLY, 0666)
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Println("warning: file not exists...")
		} else {
			fmt.Printf("open file error, err = %v\n", err)
		}
		return
	}
	// 2、延迟关闭文件
	defer file.Close()
	// 3、获取文件属性信息
	fi, err := file.Stat()
	if err != nil {
		fmt.Printf("get file info error, err = %v\n", err)
		return
	}
	fmt.Printf("file name = %v\n", fi.Name())       // file name = data.txt
	fmt.Printf("file size = %v\n", fi.Size())       // file size = 240
	fmt.Printf("file mode = %v\n", fi.Mode())       // file mode = -rw-rw-rw-
	fmt.Printf("file modTime = %v\n", fi.ModTime()) // file modTime = 2024-05-09 17:27:33.1530703 +0800 CST
	fmt.Printf("file isDir = %v\n", fi.IsDir())     // file isDir = false
	fmt.Printf("file sys = %v\n", fi.Sys())         // file sys = &{32 {1262805282 31105508} {690243707 31105523} {521554895 31105523} 0 240}
}

说明一下:

  • os包中的IsNotExist函数返回一个bool值,表示传入的错误值是否表示文件或目录不存在。
  • os包中也提供了单独的Stat函数用于获取文件的属性信息,调用时传入文件名称(包括路径)即可。

写文件

在os包中,使用File结构体的Write方法将指定内容写入到文件中,该方法的原型如下:

func (f *File) Write(b []byte) (n int, err error)

参数说明:

  • b:表示要写入文件的数据。

返回值说明:

  • n:表示成功写入文件的字节数。
  • err:如果写入过程中出错,将返回非nil的错误值。

写文件示例如下:

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	name := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\NormalOperation\data.txt`
	// 1、打开文件
	file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file error, err = %v\n", err)
		return
	}
	// 2、延迟关闭文件
	defer file.Close()
	// 3、写文件
	var str string
	for i := 0; i < 10; i++ {
		str += "Hello File(os package)" + strconv.Itoa(i) + "\n"
	}
	count, err := file.Write([]byte(str))
	if err != nil {
		fmt.Printf("write file error, err = %v\n", err)
		return
	}
	fmt.Printf("write %d bytes data to data.txt\n", count) // write 240 bytes data to data.txt
}

运行程序后可以看到数据被成功写入文件。如下:

在这里插入图片描述

读文件

在os包中,使用File结构体的Read方法读取文件中的内容,该方法的原型如下:

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

参数说明:

  • b:输出型参数,用于存储从文件中读取到的数据。

返回值说明:

  • n:表示成功读取到的字节数。
  • err:如果读取过程中出错,将返回非nil的错误值。

读文件示例如下:

package main

import (
	"fmt"
	"os"
)

func main() {
	name := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\NormalOperation\data.txt`
	// 1、打开文件
	file, err := os.OpenFile(name, os.O_RDONLY, 0666)
	if err != nil {
		fmt.Printf("open file error, err = %v\n", err)
		return
	}
	// 2、延迟关闭文件
	defer file.Close()
	// 3、获取文件大小
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("get file info error, err = %v\n", err)
		return
	}
	fileSize := fileInfo.Size()
	// 4、读文件
	buffer := make([]byte, fileSize)
	count, err := file.Read(buffer)
	if err != nil {
		fmt.Printf("read file error, err = %v\n", err)
		return
	}
	fmt.Printf("file size = %d, read count = %d\n", fileSize, count)
	fmt.Printf("file content:\n%s\n", string(buffer))
}

运行程序后可以看到文件中的数据被成功读取出来。如下:

在这里插入图片描述

其他函数和方法

在os包中,常用的打开文件获取File对象的函数如下:

函数名功能
OpenFile打开文件,可以指明打开文件的方式和新创建文件的权限
Open以只读模式打开文件
Create以0666权限创建并打开文件,如果文件已存在会将其截断

在os包中,常用的File结构体提供的文件操作方法如下:

方法名功能
Name获取文件名称(包括路径)
Stat获取文件属性信息
Fd获取文件对应的文件描述符
Read从当前位置开始读取文件数据
ReadAt从指定位置开始读取文件数据
Write从当前位置开始向文件写入数据
WriteString从当前位置开始向文件写入一个字符串
WriteAt从指定位置开始向文件写入数据
Seek设置下一次文件的读写位置
Close关闭文件

带缓冲的文件操作方式(bufio包)

bufio包介绍

  • bufio包中的Reader和Writer类型提供了带缓冲的读写功能,可以搭配os包一起使用来实现文件的带缓冲读写。

Reader结构体的定义如下:

type Reader struct {
	buf          []byte
	rd           io.Reader // reader provided by the client
	r, w         int       // buf read and write positions
	err          error
	lastByte     int // last byte read for UnreadByte; -1 means invalid
	lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}

Reader结构体各字段说明:

  • buf:内部缓冲区,用于临时存储从底层io.Reader读取到的数据。
  • rd:创建Reader对象时提供的io.Reader对象,用于读取数据。
  • r:缓冲区中的读取位置,表示下一个要读取的字节位置。
  • w:缓冲区中的写入位置,表示下一个要写入的字节位置。
  • err:存储读取数据过程中发生的错误。
  • lastByte:上一次读取的字节,用于UnreadByte方法,-1表示无效。
  • lastRuneSize:上一次读取的Unicode字符大小,用于UnreadRune方法,-1表示无效。

Reader结构体常用的方法如下:

方法名功能
Read从缓冲区中读取数据到指定的字节切片中
ReadString从缓冲区中读取数据到字符串中,直到遇到指定的分隔符
ReadLine从缓冲区读取一行数据,返回的字节切片包含行尾的换行符

Writer结构体的定义如下:

type Writer struct {
	err error
	buf []byte
	n   int
	wr  io.Writer
}

Writer结构体各字段说明:

  • err:存储写入数据过程中发生的错误。
  • buf:内部缓冲区,用于临时存储需要写入到底层io.Reader的数据。
  • n:缓冲区中的有效数据长度,表示待写入的字节数。
  • wr:创建Writer对象时提供的io.Writer对象,用于写入数据。

Writer结构体常用的方法如下:

方法名功能
Write将字节切片中的数据写入缓冲区
WriteString将字符串中的数据写入缓冲区
Flush将缓冲区中的数据刷新到底层的io.Writer

说明一下:

  • Reader结构体中的lastByte字段,用于支持UnreadByte方法,该方法用于将最后读取的字节放回缓冲区。lastRuneSize字段用于支持UnreadRune方法,该方法用于将最后读取的Unicode字符放回缓冲区。
  • Writer结构体中不需要记录下一次缓冲区的读取和写入位置,因为Writer缓冲区的写入操作是顺序进行的,不需要回退或随机访问缓冲区中的数据,而只有当缓冲区满或调用Flush方法时才会读取缓冲区中的数据,将其刷新到底层的io.Writer中,每次刷新都会读取整个缓冲区中的数据。

写文件

要使用bufio包对文件进行写入操作,首先需要创建出Writer对象,bufio包中提供了两个用于创建Writer对象的函数,它们的函数原型如下:

func NewWriter(w io.Writer) *Writer
func NewWriterSize(w io.Writer, size int) *Writer

参数说明:

  • w:创建Writer对象时提供的io.Writer对象,用于写入数据。
  • size:表示创建的Writer对象的缓冲区大小,以字节为单位。

返回值说明:

  • 返回所创建的Writer对象的指针,该对象具有指定大小的缓冲区,如果使用NewWriter函数创建Writer对象,那么缓冲区大小默认为4096字节。

使用NewWriter或NewWriterSize函数创建Writer对象时,都需要提供一个io.Writer类型的对象。io.Writer本质是io包中的一个接口类型,其定义如下:

type Writer interface {
	Write(p []byte) (n int, err error)
}

io.Writer接口中只定义了一个Write方法,因此所有实现了该Write方法的类型都可以传递给io.Writer接口。而os包中的File结构体提供的Write方法,恰好与io.Writer接口中的Write方法签名一致,因此File对象可以传递给io.Writer接口。File结构体提供的Write方法如下:

func (f *File) Write(b []byte) (n int, err error)

当bufio.Writer刷新底层缓冲区时,会回调File对象的Write方法,将缓冲区中的数据写入到文件中,完成文件的写入操作。案例如下:

package main

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

func main() {
	name := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\BufferedOperation\data.txt`
	// 1、打开文件
	file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file error, err = %v\n", err)
		return
	}
	// 2、延迟关闭文件
	defer file.Close()
	// 3、写文件
	var str string
	for i := 0; i < 10; i++ {
		str += "Hello File(bufio package)" + strconv.Itoa(i) + "\n"
	}
	writer := bufio.NewWriter(file) // 创建Writer
	count, err := writer.Write([]byte(str))
	if err != nil {
		fmt.Printf("write file error, err = %v\n", err)
		return
	}
	writer.Flush() // 刷新缓冲区
	fmt.Printf("write %d bytes data to data.txt\n", count) // write 270 bytes data to data.txt
}

运行程序后可以看到数据被成功写入文件。如下:

在这里插入图片描述

注意: 只有当Writer缓冲区满或调用Flush方法时,才会将缓冲区中的数据刷新到底层的io.Writer中,因此在数据写入Writer后需要Flush方法刷新缓冲区中的数据。

读文件

要使用bufio包对文件进行读取操作,首先需要创建出Reader对象,bufio包中提供了两个用于创建Reader对象的函数,它们的函数原型如下:

func NewReader(rd io.Reader) *Reader
func NewReaderSize(rd io.Reader, size int) *Reader

参数说明:

  • rd:创建Reader对象时提供的io.Reader对象,用于读取数据。
  • size:表示创建的Reader对象的缓冲区大小,以字节为单位。

返回值说明:

  • 返回所创建的Reader对象的指针,该对象具有指定大小的缓冲区,如果使用NewReader函数创建Reader对象,那么缓冲区大小默认为4096字节。

使用NewReader或NewReaderSize函数创建Reader对象时,都需要提供一个io.Reader类型的对象。io.Reader本质是io包中的一个接口类型,其定义如下:

type Reader interface {
	Read(p []byte) (n int, err error)
}

io.Reader接口中只定义了一个Read方法,因此所有实现了该Read方法的类型都可以传递给io.Reader接口。而os包中的File结构体提供的Read方法,恰好与io.Reader接口中的Read方法签名一致,因此File对象可以传递给io.Reader接口。File结构体提供的Read方法如下:

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

当bufio.Reader读取文件时,会回调File对象的Read方法,将文件中的数据读取到缓冲区中,完成文件的读取操作。案例如下:

package main

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

func main() {
	name := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\BufferedOperation\data.txt`
	// 1、打开文件
	file, err := os.OpenFile(name, os.O_RDONLY, 0666)
	if err != nil {
		fmt.Printf("open file error, err = %v\n", err)
		return
	}
	// 2、延迟关闭文件
	defer file.Close()
	// 3、获取文件大小
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Printf("get file info error, err = %v\n", err)
		return
	}
	fileSize := fileInfo.Size()
	// 4、读文件
	reader := bufio.NewReader(file) // 创建Reader
	buffer := make([]byte, fileSize)
	count, err := reader.Read(buffer)
	if err != nil {
		fmt.Printf("read file error, err = %v\n", err)
		return
	}
	fmt.Printf("file size = %d, read count = %d\n", fileSize, count)
	fmt.Printf("file content:\n%s\n", string(buffer))
}

运行程序后可以看到文件中的数据被成功读取出来。如下:

在这里插入图片描述

文件拷贝操作(io包)

io包介绍

在io包中,使用Copy函数能够将数据从一个io.Reader拷贝到一个io.Writer中,该函数的函数原型如下:

func Copy(dst Writer, src Reader) (written int64, err error)

参数说明:

  • dst:表示将拷贝到的数据写入到该io.Writer中。
  • src:表示从该io.Reader中拷贝数据。

返回值说明:

  • written:返回成功拷贝的字节数。
  • err:如果拷贝过程中出错,将返回非nil的错误值。

拷贝文件

前面已经介绍过,io.Reader和io.Writer本质都是io包中的接口类型,os包中的File结构体对这两个接口进行了实现,因此在使用io.Copy函数拷贝文件时,只需传入src文件和dst文件的File对象即可。案例如下:

package main

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

func CopyFile(dstName string, srcName string) (err error) {
	// 1、以读方式打开源文件
	srcFile, err := os.OpenFile(srcName, os.O_RDONLY, 0666)
	if err != nil {
		fmt.Printf("open src file error, err = %v\n", err)
		return
	}
	defer srcFile.Close()
	// 2、以写方式打开目标文件
	dstFile, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open dst file error, err = %v\n", err)
		return
	}
	defer dstFile.Close()
	// 3、进行文件拷贝
	count, err := io.Copy(dstFile, srcFile)
	if err != nil {
		fmt.Printf("copy file error, err = %v\n", err)
		return
	}
	fmt.Printf("copy file success, copy size = %d\n", count) // copy file success, copy size = 250
	return
}

func main() {
	srcName := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\CopyOperation\src.txt`
	dstName := `D:\github\Golang-topics\src\go_code\FileOperation\FileOperation\CopyOperation\dst.txt`
	err := CopyFile(dstName, srcName)
	if err != nil {
		fmt.Printf("copy file error, err = %v\n", err)
	}
}

运行程序后可以看到成功完成了文件的拷贝操作。如下:

在这里插入图片描述

命令行参数

基本介绍

基本介绍

  • 命令行参数是在运行程序时通过命令行传递给程序的参数,在Go中可以使用os包的Args变量来访问命令行参数。
  • os.Args是一个字符串切片,其中第一个元素是程序本身的名称,随后的元素依次是传递给程序的命令行参数。

使用案例如下:

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Printf("os.Args type = %T\n", os.Args) // os.Args type = []string
	for index, value := range os.Args {
		fmt.Printf("args[%d] = %v\n", index, value)
	}
}

运行上述程序时指明命令行参数,可以看到命令行参数被逐一输出。如下:

在这里插入图片描述

解析命令行参数(flag包)

flag包介绍

  • 通过os包的Args变量虽然可以访问命令行参数,但对参数的解析不是特别方便,比如各个参数的含义定义后要求严格的输入顺序。
  • flag包是Go标准库中的一个包,通过flag包可以轻松定义和解析命令行参数,并且参数的输入顺序可以随意。

flag包中常用的函数如下:

函数名功能
StringVar用于定义一个string类型的命令行参数
IntVar用于定义一个int类型的命令行参数
BoolVar用于定义一个bool类型的命令行参数
Parse用于解析命令行参数,将命令行参数的值赋给相应的变量

其中StringVar、IntVar和BoolVar函数都有四个参数,没有返回值。各个参数的含义如下:

  • 第一个参数:指向对应类型变量的指针,用于存储命令行参数的值。
  • 第二个参数:命令行参数的名称。
  • 第三个参数:命令行参数的默认值,用户未输入该命令行参数时采用。
  • 第四个参数:命令行参数的描述信息。

解析命令行参数

例如在运行mysql命令连接MySQL服务器时,需要通过命令行参数指明用户名、密码、服务端IP地址和端口号等。借助flag包可以按如下方式实现:

package main

import (
	"flag"
	"fmt"
)

func main() {
	var user, psw, ip string
	var port int
	flag.StringVar(&user, "u", "root", "用户名")
	flag.StringVar(&psw, "p", "000000", "密码")
	flag.StringVar(&ip, "h", "localhost", "IP地址")
	flag.IntVar(&port, "P", 3306, "端口号")

	flag.Parse() // 解析命令行参数

	fmt.Printf("user = %v\n", user)
	fmt.Printf("psw = %v\n", psw)
	fmt.Printf("ip = %v\n", ip)
	fmt.Printf("port = %v\n", port)
}

在运行程序时,需要通过-命令行参数名称 命令行参数的形式,依次指明各个命令行参数,如果输入的命令行参数名称未定义,则会输出一个简单的使用手册。如下:

在这里插入图片描述

只有按照正确的格式指明命令行参数后程序才能正常运行,但各个命令行参数的指明顺序可以随意。对于未指明的命令行参数,将采用定义命令行参数时给定的默认值。如下:

在这里插入图片描述

注意: 在命令行参数定义完毕后,需要调用Parse函数解析命令行参数,这样才能将命令行参数的值赋给相应的变量。

JSON

基本介绍

基本介绍

  • JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于在网络上传输数据,它最初是基于JavaScript语言的一个子集,但目前已经成为一种独立于编程语言的数据格式。
  • 不同的系统和平台通过JSON可以方便地交换和共享数据,无论其使用的是哪种编程语言,这使得JSON成为一种常用的数据交换格式,在Web开发、API设计和数据存储中得到广泛应用。
  • JSON序列化指的是将数据转换为JSON格式的过程,JSON反序列化指的是将JSON格式的数据转换为原始数据的过程。

JSON使用人类可读的文本来表示结构化数据,采用键值对的方式存储数据,其由以下几种数据类型组成:

  • 对象:由一组无序的键值对组成,使用大括号{}表示。键是字符串,值可以是字符串、数字、布尔值、对象、数组或空值。
  • 数组:由一组有序的值组成,使用中括号[]表示。值可以是字符串、数字、布尔值、对象、数组或空值。
  • 字符串:由双引号包围的Unicode字符序列。
  • 数字:整数或浮点数。
  • 布尔值:true或false。
  • 空值:null。

例如下面是一个简单的JSON字符串:

{
	"name": "Alice",
	"age": 12,
	"gender": "女",
	"scores": [
		105,
		128,
		115
	]
}

JSON序列化

JSON序列化

在Go中,使用encoding/json包中的Marshal函数能够对数据进行JSON序列化,该函数的函数原型如下:

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

参数说明:

  • v:需要进行JSON序列化的任意类型的数据。

返回值说明:

  • 第一个返回值:表示JSON序列化成功后得到的JSON字符串。
  • 第二个返回值:如果序列化过程中出错,将返回非nil的错误值。

使用案例如下:

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name   string         `json:"name"`
	Age    int            `json:"age"`
	Gender string         `json:"gender"`
	Scores map[string]int `json:"scores"`
}

func main() {
	var stu = Student{
		Name:   "Alice",
		Age:    12,
		Gender: "女",
		Scores: map[string]int{
			"语文": 105,
			"数学": 128,
			"英语": 115,
		},
	}
	data, err := json.Marshal(stu)
	if err != nil {
		fmt.Printf("json serialize error, err = %v\n", err)
		return
	}
	fmt.Printf("json str = %s\n", string(data))
}

运行程序后可以看到序列化后的JSON字符串。如下:

在这里插入图片描述

说明一下: 通过结构体字段的Tag标签,可以指定结构体各个字段在JSON序列化时的名称,如果没有指定则默认使用字段名。

JSON反序列化

JSON反序列化

在Go中,使用encoding/json包中的Unmarshal函数能够将JSON字符串反序列化为原始数据,该函数的函数原型如下:

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

参数说明:

  • data:需要进行反序列化的JSON字符串。
  • v:指向目标结构体或数据的指针,用于保存反序列化后的结果。

返回值说明:

  • 反序列化成功返回nil,否则返回非nil的错误值。

使用案例如下:

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Name   string `json:"name"`
	Age    int    `json:"age"`
	Gender string `json:"gender"`
	Scores []int  `json:"scores"`
}

func main() {
	var str = `{"name":"Alice","age":12,"gender":"女","scores":[105,128,115]}`
	var stu Student
	err := json.Unmarshal([]byte(str), &stu)
	if err != nil {
		fmt.Printf("json unserialize error, err = %v\n", err)
		return
	}
	fmt.Printf("stu = %v\n", stu)
}

运行程序后可以看到反序列化后的Student对象。如下:

在这里插入图片描述

注意: 要确保反序列化的数据类型与序列化前的数据类型一致,否则会反序列化失败。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1696109.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【手把手带你搓组件库】从零开始实现Element Plus

从零开始实现Element Plus 前言亮点项目搭建1、创建项目初始化monorepo创建 .gitignore目录结构安装基础依赖配置文件创建各个分包入口utilscomponentscoreplaytheme 2、创建VitePress文档3、部署到Github Actions生成 GH_TOKENGitHub Page 演示 4、总结 前言 在本文中&#xf…

vim操作手册

vim分为插入模式、命令模式、底行模式。 插入模式&#xff1a;编辑模式 命令模式&#xff1a;允许使用者通过命令&#xff0c;来进行文本的编辑控制 底行模式&#xff1a;用来进行让vim进行包括但不限于shell进行交互 w&#xff1a;保存 wq&am…

北邮22级信通院DSP:用C++程序实现给定参数下四种滤波器的Butterworth模拟滤波器设计:给定上下截频和衰减系数求H(p)和H(s)

北邮22信通一枚~ 跟随课程进度更新北邮信通院DSP的笔记、代码和文章&#xff0c;欢迎关注~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院DSP_青山入墨雨如画的博客-CSDN博客 目录 一、 核心算法 1.1判断滤波器类型 1.2 带通滤波器BP 1.3带阻滤波器B…

十二、shell编程之awk

12.1 什么是awk 虽然sed编辑器是非常方便自动修改文本文件的工具&#xff0c;但其也有自身的限制。通常你需要一个用来处理文件中的数据的更高级工具&#xff0c;它能提供一个类编程环境来修改和重新组织文件中的数据。这正是awk能够做到的。 awk程序是Unix中的原始awk程序的…

P4097 【模板】李超线段树 / [HEOI2013] Segment 题解

题意 有一个平面直角坐标系&#xff0c;总共 n n n 个操作&#xff0c;每个操作有两种&#xff1a; 给定正整数 x 0 , y 0 , x 1 , y 1 x_0,y_0,x_1,y_1 x0​,y0​,x1​,y1​ 表示一条线段的两个端点。你需要在平面上加入这一条线段&#xff0c;第 i i i 条被插入的线段的标…

【面试干货】完全平方数

【面试干货】完全平方数 1、实现思想2、代码实现 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 一个整数&#xff0c;它加上 100 后是一个完全平方数&#xff0c;再加上 168 又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; 1、…

设计模式 17 组合模式 Composite Pattern

设计模式 17 组合模式 Composite Pattern 1.定义 组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分整体模式&#xff0c;是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象&#xff0c;用来表示部分以及整体层次。这种类型的设…

wps使用(解决毕业论文)

目录自动生成 页码自动生成 一部分使用I II III IV 格式&#xff0c;一部分使用1&#xff0c;2&#xff0c;3&#xff0c;4 格式 先设置全部文章为I II III IV 格式&#xff0c;然后再需要的地方再设置1&#xff0c;2&#xff0c;3&#xff0c;4 格式 一键设置中文、英文、数…

特斯拉FSD的「端到端」到底能不能成?

引言 近年来&#xff0c;特斯拉的全自动驾驶&#xff08;Full Self-Driving&#xff0c;FSD&#xff09;技术备受关注&#xff0c;尤其是其「端到端」的AI软件框架更是引发了广泛讨论。端到端技术到底是一条正确的路径吗&#xff1f;它能否真正实现完全自动驾驶&#xff1f;本…

Java面试八股之什么是锁消除和锁粗化

什么是锁消除和锁粗化 锁消除&#xff08;Lock Elimination&#xff09;&#xff1a; 锁消除是Java虚拟机&#xff08;JVM&#xff09;进行的一种高级优化策略&#xff0c;旨在消除那些没有必要存在的同步操作&#xff0c;以减少不必要的性能开销。这一优化发生在即时编译器&a…

Docker拉取镜像报错:x509: certificate has expired or is not yet v..

太久没有使用docker进行镜像拉取&#xff0c;今天使用docker-compose拉取mongo发现报错&#xff08;如下图&#xff09;&#xff1a; 报错信息翻译&#xff1a;证书已过期或尚未有效。 解决办法&#xff1a; 1.一般都是证书问题或者系统时间问题导致&#xff0c;可以先执行 da…

Nginx-狂神说

Nginx概述 公司产品出现瓶颈&#xff1f; 我们公司项目刚刚上线的时候&#xff0c;并发量小&#xff0c;用户使用的少&#xff0c;所以在低并发的情况下&#xff0c;一个jar包启动应用就够了&#xff0c;然后内部tomcat返回内容给用户。 但是慢慢的&#xff0c;使用我们平台…

微信小程序- 实现横向滑动列表

1. 微信小程序-实现横向滑动列表 微信小程序如何隐藏scroll-view滚动条    1.1. photoScroll.wxml <view class"hs-body"><scroll-view class"hs-layout" scroll-x"true" scroll-left"{{x}}" scroll-with-animation&quo…

设计循环队列(C语言)怎会如此简单!!!

目录 题目题目分析 解答结构体初始化判空判满插入删除去队头数据取队尾数据队列的销毁 题目 链接: 题目 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它…

AI Agent: Agent框架+7个实例

何谓Agent Agent 作为一种新兴的人工智能技术&#xff0c;正在受到越来越多的关注。要说清楚什么是 Agent&#xff0c;先得看看人工智能的本质是什么。 人工智能这个名称来自它试图通过计算机程序或机器来模拟、扩展和增强人类智能的 一些方面。在这个定义中&#xff0c;“人…

【QGIS入门实战精品教程】10.6:QGIS制作酒店分布热力图

相关阅读: ArcGIS实验教程——实验四十二:ArcGIS密度分析(核密度、点密度、线密度) 【ArcGIS微课1000例】0086:基于七普人口数据的人口密度分析与制图 ArcGIS实验教程——实验二十四:人口密度制图 文章目录 一、加载酒店分布数据二、热力分析一、加载酒店分布数据 订阅专…

LeetCode刷题之HOT100之合并二叉树

2024/5/26 晴。是的&#xff0c;等下我要去长乐沙滩赶海哈哈&#xff0c;因为这几天数字峰会&#xff0c;地铁公交又免费啦。ok&#xff0c;今天做的是HOT100里面最后一道easy题目啦&#xff0c;明天就是要跨越一个难度啦&#xff01;做题吧 1、题目描述 2、逻辑分析 题目要求…

Python脚本必加代码:99%的程序员都忽视了这个细节!

文章目录 一、初识 if __name__ __main__二、__name__ 和 __main__ 是什么&#xff1f;三、实战讲解四、实际应用场景测试代码提高代码可重用性避免不必要的执行 五、深入理解和更多用法使用 argparse 解析命令行参数使用 unittest 进行单元测试使用 multiprocessing 创建子进…

任推邦:实力强劲的APP推广拉新平台,号称不扣量

任推邦简介 任推邦是国内数一数二的项目分发平台&#xff0c;也是一个不扣量的项目APP推广拉新平台&#xff0c;隶属于聚名科技集团股份有限公司。聚名科技成立时间在2012年&#xff0c;是安徽省老牌互联网企业&#xff0c;历经11年的飞速发展&#xff0c;聚名科技成功布局打造…

Adobe Bridge BR v14.0.3 安装教程 (多媒体文件组织管理工具)

Adobe系列软件安装目录 一、Adobe Photoshop PS 25.6.0 安装教程 (最流行的图像设计软件) 二、Adobe Media Encoder ME v24.3.0 安装教程 (视频和音频编码渲染工具) 三、Adobe Premiere Pro v24.3.0 安装教程 (领先的视频编辑软件) 四、Adobe After Effects AE v24.3.0 安装…