热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mysqljson中文_MySQL对JSON类型UTF8编码导致中文乱码探讨

前言继上文发表之后,结合评论意见并亲自验证最终发现是编码的问题,但是对于字符编码还是有点不解,于是乎,有了本文,

前言

继上文发表之后,结合评论意见并亲自验证最终发现是编码的问题,但是对于字符编码还是有点不解,于是乎,有了本文,我们来学习字符编码,在学习的过程中,我发现对于MySQL中JSON类型的编码导致数据中文出现乱码还有可深挖之处,接下来我们来分析一下,若有错误之处,还请批评指出。

字符编码

评论中指出任何不在基本多文本平面的Unicode字符,都无法使用MySQL的utf8字符集存储,包括Emoji 表情(Emoji 是一种特殊的Unicode 编码,常见于IOS和Android 手机上)和很多不常用的汉字,以及任何新增的 Unicode 字符等等(utf8的缺点),然而啥是多文本平面,详情维基百科《https://en.wikipedia.org/wiki/Plane_(Unicode)》。首先我们了解下什么是Unicode,Unicode是通用字符集,它是一种标准,该标准在一处定义了编写在计算机上使用的大多数活动语言所需的所有字符,它的目标是成为并且在很大程度上已经是已编码的所有其他字符集的超集。在计算机或网络中的文本我们通过字符组成,字符代表字母、标点符号或其他符号。不同的组织收集了不同的字符集并为其创建了编码-一个字符集可能仅覆盖基于拉丁语的西欧语言(不包括保加利亚或希腊等欧盟国家),另一个可能覆盖特定的远东语言(例如(例如日语),其他语言可能是以特殊方式设计的,代表世界上某处其他语言的众多语言之一。但是我们并不能保证应用程序将支持所有编码,也不能保证给定的编码将满足我们代表给定语言的所有需求,另外,通常不可能在同一网页或数据库中组合不同的编码,因此使用“传统”编码方法来支持多语言页面通常非常困难,所以Unicode协会提供了一个大的,单字节字符集,旨在包括所有需要的世界上任何书写系统,包括古老的脚本(如楔形文字,哥特式和埃及的象形文字)的字符,所以统一字符编码,将其作为Web和操作系统体系结构的基础,并且得到所有主要Web浏览器和应用程序的支持。当前的Unicode字符分为17组编排,每组被称之为一个平面(Plane),所以将字符划分为0-16号的平面,而每平面拥有65536(即216)个代码点即范围区间在0x000到0xFFFF之间,而0号平面就是基本多语言平面(BMP:Basic Mutiingual Plane)。在基本多文本平面上针对每一种文字或者其补充或者其扩展都给出了一个编码范围,比如拉丁文【0000-007F】,拉丁文-补充【0080-00FF】等等。说了这么多,我们只需要记住一点即可:在Unicode字符集中前65536个代码点构成了基本多语言平面简称BMP,BMP中包含了大多常用的字符,另外Unicode字符集还包含了一百万个其他代码点的位置空间,我们称之为补充字符。我们需要区分字符集、编码字符集和编码的概念,字符集或字符串包含可能用于特定目的的字符集,它是支持计算机上的西欧语言所需的字符集,与计算机完全无关,而编码字符集是一组用于该唯一的号码被分配给每个字符的字符,有时候我们将编码字符集也可称作为代码页,编码字符集的单位称为代码点,代码点值表示字符在编码字符集中的位置。例如,Unicode编码字符集中字母á的代码点为十进制225,十六进制表示法为0xE1。而字符编码反映编码字符集被映射到用于在计算机操纵字节的方式。一个字符集可以有多种编码,许多字符编码标准,例如ISO 8859系列中定义的标准,都为给定字符使用单个字节,并且编码是对编码字符集中字符标量位置的直接映射。例如,ISO 8859-1编码字符集中的字母A在第65个字符位置(从零开始),并且使用值为65的字节进行编码并以此在计算机中表示,对于ISO 8859-1而言,这将永远不会再改变,但是,对于Unicode,事情并没有如此简单,尽管Unicode编码字符集中字母á的代码点始终为225(十进制),但在UTF-8中,它在计算机中由两个字节表示,换句话说,在此字符的编码字符集值和编码值之间不是简单的一对一映射,另外,在Unicode中,针对同一字符可以有多种编码的方式。例如,字母á可以用一种编码形式的两个字节表示,而用另一种编码形式的四个字节表示。可以与Unicode一起使用的编码形式称为UTF-8,UTF-16和UTF-32。

ccdc2caecd71d6f8b76acf66fb51b16d.png

UTF-8使用1个字节表示ASCII集中的字符,使用2个字节表示其他几个字母块中的字符,使用3个字节表示BMP的其余部分,补充字符使用4个字节。UTF-16对BMP中的任何字符使用2个字节,对补充字符使用4个字节。UTF-32对所有字符使用4个字节。基本多语言平面对应代码点存储的是常用字符,上述针对不同字符在其对应代码点,然后计算出该字符的16进制的字符串,举个栗子,将【好】字进行UTF-8编码看看该字符的字节值和字节数,如下:

var bytes = Encoding.UTF8.GetBytes("好");var hexString = BitConverter.ToString(bytes);

f9141d5c336962ac85b2c170e27bd865.png

到此我们大概了解完了字符编码,接下来我们再次回到上一节的问题,上一节将我姓名作为JSON存储到数据库中去,但是最终获取数据时,将出现乱码,因为其表编码为utf8,最终将表编码修改为utf8mb4才好使,为啥utf8就不行呢?通过上述对utf8的定义最多可以有4个字节,支持补充字符,所以MySQL根本就没有实现标准的utf8编码,换句话说只是部分实现了utf8编码,MySQL中的utf8又名为utf8mb3,也就是一个字符最多可通过3个字节表示且包含BMP字符,而不包含补充字符。所以无论是我的姓还是名虽然是3个字节,但是并非常用BMP字符导致。但是针对列类型为JSON类型,事实是对于获取中文真的会乱码吗?上文我用到的MySQL版本为5.7+,如下:

8ec673e9055c5ca6af42f177ccd228e0.png

接下来我们利用MySQL 8.0再来进行测试发现不会乱码,创建类和表配置编码如下:

0c274e9eab84aa690e4df1291eef02b1.png

public classt1

{public int id { get; set; }public string jdoc { get; set; }

}

d3ca287d0af2bcd838ba24344616235f.png

56706d94203c4024f4c9be4a6cbedd22.png

static void Main(string[] args)

{

SetDialect(Dialect.MySQL);var con = new MySqlConnection(@"Server=localhost;Database=user;Uid=root;Pwd=root;");var id = con.Insert(new t1() { jdoc = JsonConvert.SerializeObject(new { Data = "汪鹏"}) });var result = con.QueryFirstOrDefault("select * from t1 where id = @id", new{ id });

Console.ReadKey();

}

05e99e72a76c1ddd8578e36d5eed0603.png

随着移动端的兴起,有了表情的出现,所以从MySQL 5.5.3开始,引入utf8mb4字符集每个字符最多可使用4个字节,支持补充字符,对于BMP字符,utf8 [utf8mb3]和utf8mb4具有相同的存储特征:相同的代码值,相同的编码,相同的长度,对于补充字符,utf8 [utf8mb3]根本无法存储该字符,而utf8mb4需要4个字节来存储它,由于utf8 [utf8mb3]根本无法存储字符,因此在utf8 [utf8mb3]列中没有任何补充字符。接下来我们在针对JSON类型配置为utf8编码的情况下,我们来插入表情,此时会发现也是可以的。我们是可以获取对应字符的字节数,比如如下哭笑不得的表情为4个字节:

var emotion = Encoding.UTF8.GetByteCount("😂");

b38a815613a05c4a63947e1af43cd884.png

d79cc2a28eaf7c5fb2da62340064cd55.png

其实针对JSON类型获取数据乱码的情况早就有人提出过相关bug,详见地址《https://bugs.mysql.com/bug.php?id=81677》,不过官方一直没有任何回复,至少通过上述测试出来的结果对于utf8存储表情也可以,到底具体情况咋回事,我们还是看看8.0版本以对utf8编码描述为准,详情请见《https://dev.mysql.com/doc/refman/8.0/en/charset-unicode.html》,对于utf8编码的描述依然还是最多可存储3个字节,如下:

37da3f4daef757da665c03a3b196357b.png

别忘记,还有注意:utf8[utf8mb3]字符集已被弃用,并会在将来的MySQL版本中移除,请改用utf8mb4,尽管utf8当前是utf8mb3的别名,但在某些时候utf8将成为对utf8mb4的引用,为避免对utf8的含义含糊不清,请考虑为字符集引用显式指定utf8mb4而不是utf8。所以到此我们已明了,针对8.0版本中的utf8编码虽说最多可支持3个字节,但是,会将utf8成为utf8mb4的引用,如此就不难理解为何上述将表配置为utf8编码时,对于JSON类型的不在常用BMP字符进行数据存储和表情皆没问题。

总结

通过此文对utf8编码的JSON类型的数据出现中文乱码的问题的学习才算告一段落, 原来版本问题使得utf8存在对utf8mb4编码的引用,知其然,知其所以然,嗯,大概是这么个道理。发表博客的好处就在这里,没有批评和指正,哪来的更进一步呢。



推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
author-avatar
天佑土土土
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有