当前位置: 首页 > 编程日记 > 正文

2.4 Go语言基础之切片

本文主要介绍Go语言中切片(slice)及它的基本使用。

一、引子

因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。 例如:

func arraySum(x [3]int) int{sum := 0for _, v := range x{sum = sum + v}return sum
}

这个求和函数只能接受[3]int类型,其他的都不支持。 再比如,

a := [3]int{1, 2, 3}

数组a中已经有三个元素了,我们不能再继续往数组a中添加新元素了。

二、切片

2.1 切片知多少

切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容

切片与数组相比,不需要设定长度,在[]中不用设定值,相对来说比较自由。

slice本身没有数据(切片并不存储任何元素而只是对现有数组的引用。),是对底层array的一个view对slice所做的任何修改都将反映在底层数组中。

切片是什么?一种数据结构,类似数组,围绕动态数组的概念而设计,可按需自动改变大小。

切片内部实现?切片基于数组实现,底层是数组(故底层的内存是连续分配,可根据索引获取数据,可迭代以及垃圾回收),自身非常小(切片对象只有3个字段数据结构即:指向底层数组的指针ptr(内存地址)、切片长度len、切片容量cap),可看作对底层数组的抽象。

切片是一个引用类型,它的内部结构包含地址长度容量。切片一般用于快速地操作一块数据集合。

切片三要素:

  • 地址(切片中第一个元素指向的内存空间)
  • 大小(切片中目前元素的个数) len()
  • 容量(底层数组最大能存放的元素的个数) cap()

2.2 切片定义

定义声明切片类型的基本语法如下:

var name []T

其中,

  • name:表示变量名
  • T:表示切片中的元素类型

2.3 切片初始化

2.3.1 为什么要初始化?

简单地说,定义声明只是告诉我的样子(类型) 初始化就是告诉我在哪里(分配内存) 要想找到我 仅仅知道我的样子是没用的 得知道我的地址

2.3.2 方法1 定义声明切片变量时初始化

我们可以直接在声明定义切片变量时对其直接进行初始化

例1:

func main() {// 声明切片类型var a []string              //声明一个字符串切片var b = []int{}             //声明一个整型切片并初始化var c = []bool{false, true} //声明一个布尔切片并初始化//var d = []bool{false, true} //声明一个布尔切片并初始化fmt.Println(a)              //[]fmt.Println(b)              //[]fmt.Println(c)              //[false true]fmt.Println(a == nil)       //truefmt.Println(b == nil)       //falsefmt.Println(c == nil)       //false// fmt.Println(c == d)   //切片是引用类型,不支持直接比较,只能和nil比较
}

2.3.3 方法2 基于数组定义切片

由于切片的底层就是一个数组,所以我们可以基于数组定义切片。

func main() {// 基于数组定义切片a := [5]int{55, 56, 57, 58, 59}b := a[1:4]                     //基于数组a创建切片,包括元素a[1],a[2],a[3]fmt.Println(b)                  //[56 57 58]fmt.Printf("type of b:%T\n", b) //type of b:[]int
}

还支持如下方式:

c := a[1:] //[56 57 58 59]
d := a[:4] //[55 56 57]
e := a[:]  //[55 56 57 58 59]

总结如下:

a) arr[start:end]:包括start到end-1(包括end-1)之间的所有元素

b) arr[start:]:包括start到arr最后一个元素(包括最后一个元素)之间的所有元素

c) arr[:end]:包括0到end-1(包括end-1)之间的所有元素

d) arr[:]:包括整个数组的所有元素

2.3.4 方法3 基于切片再切片

除了基于数组得到切片,我们还可以通过切片来得到切片。

func main() {//切片再切片a := [...]string{"北京", "上海", "广州", "深圳", "成都", "重庆"}fmt.Printf("a:%v type:%T len:%d  cap:%d\n", a, a, len(a), cap(a))b := a[1:3]fmt.Printf("b:%v type:%T len:%d  cap:%d\n", b, b, len(b), cap(b))c := b[1:5]fmt.Printf("c:%v type:%T len:%d  cap:%d\n", c, c, len(c), cap(c))
}

输出:

a:[北京 上海 广州 深圳 成都 重庆] type:[6]string len:6  cap:6
b:[上海 广州] type:[]string len:2  cap:5
c:[广州 深圳 成都 重庆] type:[]string len:4  cap:4

注意: 对切片进行再切片时,索引不能超过原数组的长度,否则会出现索引越界的错误。

2.3.5 方法4 使用make函数构造切片

我们上面都是基于数组来创建的切片,如果需要动态的创建一个切片,我们就需要使用内置的make()函数,格式如下:

make([]T, size, cap)

其中:

  • T:切片的元素类型
  • size:切片中元素的数量
  • cap:切片的容量

举个例子:

func main() {a := make([]int, 2, 10)fmt.Println(a)      //[0 0]fmt.Println(len(a)) //2fmt.Println(cap(a)) //10
}

上面代码中a的内部存储空间已经分配了10个,但实际上只用了2个。 容量并不会影响当前元素的个数,所以len(a)返回2,cap(a)则返回该切片的容量。

补充:

make(make是一个内置函数)创建切片(其也是基于数组,只不过是底层已经创建好了,我们看不见)比较推荐这种,应用范围也比较广。切片元素的默认值为0。

make在这里做的就是在底层做了一个数组并且分配内存空间,切片然后去引用这个数组。

2.3.6 注意

  1. 空切片必须初始化,或者用append才能使用,否则就会panic。

    例1:

    package mainimport "fmt"func main() {var a []stringif a == nil {fmt.Println("a是空切片")}a[0] = "paul" //不初始化直接赋值操作fmt.Println(a)
    }

    执行结果:

    1556009465846

    例2:

    package mainimport "fmt"func main() {var a []stringif a == nil {fmt.Println("a是空切片")}a = append(a, "harden") //不初始化但是可以进行append操作,append会自动对这个空切片做自动扩容。fmt.Println(a)
    }

    执行结果:

    1556009777723

2.4切片的本质

切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。

举个例子,现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[:5],相应示意图如下。
slice_01
切片s2 := a[3:6],相应示意图如下:
slice_02

2.5 nil切片和空切片区别

nil切片(比如: var nilSlice []int )指向底层数组的指针为nil,nil切片表示不存在的切片。

空切片(比如: slice := []int{} )对应的指针是个地址,空切片表示一个空集合。

nil切片和空切片区别

  1. 有无初始化(就是有无分配内存地址),nil切片无初始化,空切片初始化了;

  2. 打印出来内容虽然都是空,但是nil切片打印为空内存地址,空切片打印为特殊的内存地址(所有类型空切片都存放在这个特殊内存地址)

  3. nil切片和空切片都可以进行append操作,因为append操作会对该切片做自动初始化扩容操作;

关于nil切片和空切片区别:http://blog.itpub.net/31561269/viewspace-2220962/

例1:

package mainimport "fmt"func main() {//空切片b := []int{}fmt.Printf("b为:%#v\n", b)   //%#v表示以go语言语法形式显示值fmt.Printf("b的内存地址:%p\n", b)b = append(b, 2)fmt.Printf("After:b为:%#v\n", b)fmt.Printf("After:b的内存地址:%p\n", b)c := []string{}fmt.Printf("c为:%#v\n", c)fmt.Printf("c的内存地址:%p\n", c)c = append(c, "MVP")fmt.Printf("After:c为:%#v\n", c)fmt.Printf("After:c的内存地址:%p\n", c)//nil切片var d []intfmt.Printf("d为:%#v\n", d)fmt.Printf("d的内存地址:%p\n", d)d = append(d, 1)fmt.Printf("After:d为:%#v\n", d)fmt.Printf("After:d的内存地址:%p\n", d)}

执行结果:

1556180139715

2.6 切片不能直接比较

切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和nil比较。 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil,例如下面的示例:

var s1 []int         //len(s1)=0;cap(s1)=0;s1==nil
s2 := []int{}        //len(s2)=0;cap(s2)=0;s2!=nil
s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil

2.7 如何使用切片?

类似使用数组,通过索引获取切片对应元素的值,也可修改对应元素的值。

切片只能访问到长度内的元素,长度外的如果访问会运行时异常。就会panic(报越界(panic: runtime error: index out of range)错误)

slice := []int{1,2,3,4,5}
fmt.Println(slice[2])  //3
slice[2] = 10 
fmt.Println(slice[2])  //10

2.8 切片的赋值拷贝

下面的代码中演示了拷贝前后两个变量共享底层数组,对一个切片的修改会影响另一个切片的内容,这点需要特别注意。

func main() {s1 := make([]int, 3) //[0 0 0]s2 := s1             //将s1直接赋值给s2,s1和s2共用一个底层数组s2[0] = 100fmt.Println(s1) //[100 0 0]fmt.Println(s2) //[100 0 0]
}

2.9 切片遍历

切片的遍历方式和数组是一致的,支持索引遍历和for range遍历。

func main() {s := []int{1, 3, 5}for i := 0; i < len(s); i++ {fmt.Println(i, s[i])}for index, value := range s {fmt.Println(index, value)}
}

2.10 append()方法为切片添加元素

Go语言的内建函数append()可以为切片动态添加元素。 每个切片会指向一个底层数组,这个数组能容纳一定数量的元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时。 举个例子:

func main() {//append()添加元素和切片扩容var numSlice []intfor i := 0; i < 10; i++ {numSlice = append(numSlice, i)fmt.Printf("%v  len:%d  cap:%d  ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)}
}

输出:

[0]  len:1  cap:1  ptr:0xc0000a8000
[0 1]  len:2  cap:2  ptr:0xc0000a8040
[0 1 2]  len:3  cap:4  ptr:0xc0000b2020
[0 1 2 3]  len:4  cap:4  ptr:0xc0000b2020
[0 1 2 3 4]  len:5  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5]  len:6  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5 6]  len:7  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5 6 7]  len:8  cap:8  ptr:0xc0000b6000
[0 1 2 3 4 5 6 7 8]  len:9  cap:16  ptr:0xc0000b8000
[0 1 2 3 4 5 6 7 8 9]  len:10  cap:16  ptr:0xc0000b8000

从上面的结果可以看出:

  1. append()函数将元素追加到切片的最后并返回该切片。
  2. 切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍。

append()函数还支持一次性追加多个元素。 例如:

var citySlice []string
// 追加一个元素
citySlice = append(citySlice, "北京")
// 追加多个元素
citySlice = append(citySlice, "上海", "广州", "深圳")
// 追加切片
a := []string{"成都", "重庆"}
citySlice = append(citySlice, a...)
fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]

2.11 切片的扩容策略

可以通过查看$GOROOT/src/runtime/slice.go源码,其中扩容相关代码如下:

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {newcap = cap
} else {if old.len < 1024 {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {newcap += newcap / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}
}

从上面的代码可以看出以下内容:

  • 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。
  • 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap),
  • 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
  • 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。

需要注意的是,切片扩容还会根据切片中元素的类型不同而做不同的处理,比如intstring类型的处理方式就不一样。

2.12 使用copy()函数复制切片

首先我们来看一个问题:

func main() {a := []int{1, 2, 3, 4, 5}b := afmt.Println(a) //[1 2 3 4 5]fmt.Println(b) //[1 2 3 4 5]b[0] = 1000fmt.Println(a) //[1000 2 3 4 5]fmt.Println(b) //[1000 2 3 4 5]
}

由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。

Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式如下:

copy(destSlice, srcSlice []T)

其中:

  • srcSlice: 数据来源切片
  • destSlice: 目标切片

举个例子:

func main() {// copy()复制切片a := []int{1, 2, 3, 4, 5}c := make([]int, 5, 5)copy(c, a)     //使用copy()函数将切片a中的元素复制到切片cfmt.Println(a) //[1 2 3 4 5]fmt.Println(c) //[1 2 3 4 5]c[0] = 1000fmt.Println(a) //[1 2 3 4 5]fmt.Println(c) //[1000 2 3 4 5]
}

2.13 从切片中删除元素

Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。 代码如下:

func main() {// 从切片中删除元素a := []int{30, 31, 32, 33, 34, 35, 36, 37}// 要删除索引为2的元素a = append(a[:2], a[3:]...)fmt.Println(a) //[30 31 33 34 35 36 37]
}

总结一下就是:要从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)

转载于:https://www.cnblogs.com/forever521Lee/p/10770349.html

相关文章:

java 文件下载 【学习记录】

工具类 public static Boolean downloadExcelFile(HttpServletResponse response, String fileName) {OutputStream output;File file new File(fileName);if (file.exists()) {try {FileInputStream fileInputStream new FileInputStream(file);BufferedInputStream buffe…

[UI自动化]:控制浏览器操作

控制浏览器窗口大小 . PC端执行自动化测试脚本大多的情况下是希望浏览器在全屏幕模式下执行&#xff0c;那么可以使用maximize_window()方法使打开的浏览器全屏显示 控制浏览器后退、前进 在使用浏览器浏览网页时&#xff0c;浏览器提供了后退和前进按钮&#xff0c;可以方便地…

在项目中使用Google Closure Compiler

现在的Web项目总是离不开大量JavaScript&#xff0c;而JS文件的体积也越来越大&#xff0c;也越来越影响页面的感知性能&#xff08;Perceived Performance&#xff09;。因此&#xff0c;我们会对JS文件进行压缩&#xff0c;一方面是使用Gzip&#xff0c;而另一方面则是去除JS…

文件服务器共享目录设置(二)

三、 设置磁盘配额及文件屏蔽 为了防止用户无限制的上传文件&#xff0c;或上传病毒木马等文件&#xff0c;还需要进一步加强安全设置。用磁盘配额来管理用户的文件夹空间&#xff0c;用文件屏蔽来阻止用户上传有风险的文件。 在win2003中&#xff0c;磁盘配额只能…

Codeforces 894.D Ralph And His Tour in Binary Country

D. Ralph And His Tour in Binary Countrytime limit per test2.5 secondsmemory limit per test512 megabytesinputstandard inputoutputstandard outputRalph is in the Binary Country. The Binary Country consists of n cities and (n - 1) bidirectional roads connect…

linux下获取系统时间 和 时间偏移

获取linux时间 并计算时间偏移 void getSystemTimer(void){#if 0 char *wdate[]{"Sun","Mon","Tue","Wed","Thu","Fri","Sat"} ; time_t timep; struct tm *p; time(&timep); pgmtime(&timep)…

使用git命令上传本地文件到GitHub上

1、官网下载git并且anz安装 2、在Github上申请账号 3、在本地使用git命令生成私钥和公钥 连续按三次 回车键 $ ssh-keygen -t rsa -C "账号"生成如图 将生成的公钥设置到github上 &#xff0c; 生成公钥的地址是 ./ssh &#xff08;C盘的用户目录&#xff09; 4…

POI解析Excel文件工具类

/*** 读取excel数据*/public static List<Map<String, Object>> exportListFromExcel(File file, int sheetNum) throws IOException { return exportListFromExcel(new FileInputStream(file), FilenameUtils.getExtension(file.getName()), sheetNum); } publ…

关于Bulk加载模式

Bulk加载模式是Informatica提供的一种高性能数据加载模式&#xff0c;它利用数据库底层机制&#xff0c;依靠调用数据库本身提供的Utility来进行数据的加载  该方式将绕过数据库的log记录&#xff0c;以此提高数据库加载性能&#xff0c;因此Bulk模式不能进行数据的Rollback操…

C#:CsvReader读取.CSV文件(转换成DataTable)

原文引用&#xff1a;https://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader using LumenWorks.Framework.IO.Csv;using System;using System.Collections.Generic;using System.Data;using System.IO;using System.Linq;using System.Text;using System.Threading.Tas…

Standup Timer的MVC模式及项目结构分析

前言 学习android一段时间了&#xff0c;为了进一步了解android的应用是如何设计开发的&#xff0c;决定详细研究几个开源的android应用。从一些开源应用中吸收点东西&#xff0c;一边进行量的积累&#xff0c;一边探索android的学习研究方向。这里我首先选择了jwood的Standup …

ISDN的配置

ISDN的配置 1、 实验目的&#xff1a; 通过配置ISDN使两个路由器之间能够通信 2、 实验拓扑图&#xff1a; 3、 实验步骤&#xff1a; &#xff08;1&#xff09; R1的配置 Router> R1的ISDN配置 Router>en R…

5,ORM组件XCode(动手)

本篇才真正是XCode教程第一篇。《速览》是为了以最简洁的语言最短小的篇幅去吸引开发者&#xff1b;《简介》则是对XCode组件和XCode开发模式的一个整体介绍&#xff0c;让开发者从宏观的角度去理解XCode&#xff1b;《共舞》把XCode提到了一个新的高度&#xff0c;让开发者感受…

前端知识分享.md

目录 一些支持固定列和表头的组件库element UIiViewAmaze UIlayui一些比较好的IDEMVVM 框架VueNode生态其它SassTypeScript一些支持固定列和表头的组件库 element UI https://element.eleme.cn/#/zh-CN/component/table iView https://www.iviewui.com/components/table Amaze …

C 输入 输出

C 输入 & 输出 当我们提到输入时&#xff0c;这意味着要向程序填充一些数据。输入可以是以文件的形式或从命令行中进行。C 语言提供了一系列内置的函数来读取给定的输入&#xff0c;并根据需要填充到程序中。 当我们提到输出时&#xff0c;这意味着要在屏幕上、打印机上或任…

搭建服务器环境 安装jdk、mysql、Tomcat 以及配置https 记录

1.在cenos上安装 jdk #在usr/local下创建 java 文件夹 mkdir java#将jdk拷贝到该文件夹中 [rootVM_0_15_centos jdk1.8.0_181]# cp jdk-8u181-linux-x64.tar.gz /usr/local/java/#解压该文件 [rootVM_0_15_centos jdk1.8.0_181]# tar -zxvf jdk-8u181-linux-x64.tar.gz #配置环…

一个Java程序员应该掌握的10项技能

1、语法&#xff1a;必须比较熟悉&#xff0c;在写代码的时候IDE的编辑器对某一行报错应该能够根据报错信息知道是什么样的语法错误并且知道任何修正。 2、命令&#xff1a;必须熟悉JDK带的一些常用命令及其常用选项&#xff0c;命令至少需要熟悉&#xff1a;appletviewer、 Ht…

逆向-攻防世界-maze

题目提示是走迷宫。 IDA载入程序分析。 输入字符长度必须是24&#xff0c;开头必须是nctf{&#xff0c;结尾必须是}。在125处按R就可以变成字符。 sub_400650和sub_400660是关键函数&#xff0c;分析sub_400650。 v10的下一字节减1. sub_400660v10的下一字节加1. 分析这两个函数…

linxu passwd 给linux用户设置密码 命令

[rootlocalhost ~]# passwd # 修改 root 用户的密码passwd 给linux用户设置密码 命令 passwd www 直接passwd是当前用户设置密码 非交互式修改密码&#xff1a; echo "123456" |passwd --stdin www [rootMongoDB ~]# echo "123456" | passwd…

MultipartFile 使用 记录

MultipartFile file 获取文件流 file.getInputStream()&#xff1b; 获取文件名称 String fileName file.getOriginalFilename(); 待补充

多语言加载图片问题

系统要求多语言&#xff1a;中文和英文&#xff0c;分别在两个xml文件中书写相关内容&#xff0c;类似&#xff1a; <root><resource name"VersionName">Version</resource><resource name"Logout">Logout</resource></r…

5.html基础标签:块级+行级元素+特殊字符+嵌套规则

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title> </head> <body><!-- 块元素&#xff1a;独立成一行(相当于标签前后都设置了br),可以设置宽高,默认宽高100%文字…

[摘]终于找到一个有助理解left/right/full outer join的例子

近日在学习《Understading DB2》的时候找到了一个例子&#xff0c;对于理解 left/right/full 三种 outer join 的大有裨益。 先看样本数据&#xff0c;来自DB2的示例数据库 sample&#xff1a; db2 > insert into employee values(99999,killkill,N,Huang,null,null,null,no…

vue——props的两种常用方法

vue——props的两种常用方法 1、实现父——>子的通信 举例如下&#xff1a; 父组件 parent.vue<children :channel"object1"></children> 子组件 children.vue export default{name:"children",props:["channel"],data(){return{…

idea导入模板

1.我的配置文件下载 请点下面的连接 https://download.csdn.net/download/huyande123/10728802 先导入第二个文件&#xff0c;在导入第一个文件 在此基础上添加了类注释和方法注释&#xff08;方法注释快捷键 qtab&#xff09; 参考链接 https://blog.csdn.net/qq_34581118…

为office添加繁简体转换

为office添加繁简体转换本人所在的公司是一间港资公司&#xff0c;很多香港同事习惯繁体&#xff0c;而我们内地的同事则习惯简体&#xff0c;于是免不了要进行繁简体转换&#xff0c;这时候就要装一个office的插件来达到这样的功能&#xff0c;如下&#xff1a;1、没装插件的时…

关闭Windows 7中的 Program Compatibility Assistant

感觉微软总喜欢把简单问题复杂化。安装几个小软件也老是弹出这样的对话框&#xff1a; 然后点击“What settings are applied?”&#xff0c;看到帮助中一段&#xff1a; 提示我在组策略里能够关闭这个烦人的程序兼容性助手&#xff0c;却没有明说&#xff0c;故意卖关子呢。那…

Swift学习:自动引用计数

swift 使用自动引用计数&#xff08;ARC&#xff09;机制来跟踪和管理你的应用程序的内存。通常情况下&#xff0c;swift 内存管理机制会一直起作用&#xff0c;你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时&#xff0c;自动释放其占用的内存。 然而在少数情况下…

简单又实用的分享!SharePoint母版页引用(实战)

分享人&#xff1a;广州华软 极简 一. 前言 此SharePoint 版本为2013&#xff0c;请注意版本号。此文以图文形式&#xff0c;描述了根网站及子网站引用母版页&#xff0c;需要注意的点已用图文形式以标明。 本文适用于初学者。 二. 目录 1. 前言 2. 目录 3. 如何引用母版页 4. …

mysql 修改某字段的格式为 utf8mb4

ALTER TABLE confession MODIFY content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;#查看表中字段字符集 SHOW FULL COLUMNS FROM confession;