关于字符集:故障分析-MySQL-convert-函数导致的字符集报错处理

作者:徐耀荣 爱可生南区交付服务部 DBA 团队成员,次要负责MySQL故障解决以及相干技术支持。喜好电影,游戏,游览以及桌球。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 一、问题背景有客户之前遇到一个 mysql8.0.21 实例中排序规定的报错,是在调用视图时抛出,报错信息如下: ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '=' 二、问题模仿mysql> show create table t1\G;*************************** 1. row *************************** Table: t1Create Table: CREATE TABLE `t1` ( `name1` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci1 row in set (0.00 sec)mysql> show create table t2\G;*************************** 1. row *************************** Table: t2Create Table: CREATE TABLE `t2` ( `name2` varchar(12) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin11 row in set (0.00 sec)mysql> CREATE VIEW t3 as select * from t1,t2 where `t1`.`name1`= `t2`.`name2`;Query OK, 0 rows affected (0.06 sec)mysql> select * from t3;ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='三、问题剖析通过查看视图定义,能够发现因为视图中波及到的两张表字符集不同,所以创立视图时 MySQL 会主动应用 convert 函数转换字符集。 ...

February 9, 2023 · 4 min · jiezi

关于字符集:字符编码原理

ASCII 编码在计算机外部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因而八个二进制位就能够组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共能够用来示意256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。 上个世纪60年代,美国制订了一套字符编码,对英语字符与二进制位之间的关系,做了对立规定。这被称为 ASCII 码,始终沿用至今。ASCII 码一共规定了128个字符的编码,比方空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包含32个不能打印进去的管制符号),只占用了一个字节的前面7位,最后面的一位对立规定为0 非ASCII 编码英语用128个符号编码就够了,然而用来示意其余语言,128个符号是不够的。比方,在法语中,字母上方有注音符号,它就无奈用 ASCII 码示意。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比方,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家应用的编码体系,能够示意最多256个符号。 然而,这里又呈现了新的问题。不同的国家有不同的字母,因而,哪怕它们都应用256个符号的编码方式,代表的字母却不一样。比方,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (),在俄语编码中又会代表另一个符号。然而不管怎样,所有这些编码方式中,0--127示意的符号是一样的,不一样的只是128--255的这一段。 至于亚洲国家的文字,应用的符号就更多了,汉字就多达10万左右。一个字节只能示意256种符号,必定是不够的,就必须应用多个字节表白一个符号。比方,简体中文常见的编码方式是 GB2312,应用两个字节示意一个汉字,所以实践上最多能够示意 256 x 256 = 65536 个符号。 Unicode 编码世界上存在着多种编码方式,同一个二进制数字能够被解释成不同的符号。因而,要想关上一个文本文件,就必须晓得它的编码方式,否则用谬误的编码方式解读,就会呈现乱码。为什么电子邮件经常呈现乱码?就是因为发信人和收信人应用的编码方式不一样。 如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个举世无双的编码,那么乱码问题就会隐没。这就是 Unicode,就像它的名字都示意的,这是一种所有符号的编码。 Unicode 当然是一个很大的汇合,当初的规模能够包容100多万个符号。每个符号的编码都不一样,比方,U+0639示意阿拉伯字母Ain,U+0041示意英语的大写字母A,U+4E25示意汉字严。具体的符号对应表,能够查问unicode.org,或者专门的汉字对应表。 Unicode 的问题Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。 比方,汉字严的 Unicode 是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说,这个符号的示意至多须要2个字节。示意其余更大的符号,可能须要3个字节或者4个字节,甚至更多。 这里就有两个重大的问题,第一个问题是,如何能力区别 Unicode 和 ASCII ?计算机怎么晓得三个字节示意一个符号,而不是别离示意三个符号呢?第二个问题是,咱们曾经晓得,英文字母只用一个字节示意就够了,如果 Unicode 对立规定,每个符号用三个或四个字节示意,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的节约,文本文件的大小会因而大出二三倍,这是无奈承受的。 它们造成的后果是:1)呈现了 Unicode 的多种存储形式,也就是说有许多种不同的二进制格局,能够用来示意 Unicode。2)Unicode 在很长一段时间内无奈推广,直到互联网的呈现。 UTF-8 编码互联网的遍及,强烈要求呈现一种对立的编码方式。UTF-8 就是在互联网上应用最广的一种 Unicode 的实现形式。其余实现形式还包含 UTF-16(字符用两个字节或四个字节示意)和 UTF-32(字符用四个字节示意),不过在互联网上根本不必。反复一遍,这里的关系是,UTF-8 是 Unicode 的实现形式之一。 UTF-8 最大的一个特点,就是它是一种变长的编码方式。它能够应用1~4个字节示意一个符号,依据不同的符号而变动字节长度。 UTF-8 的编码规定很简略,只有二条: 1)对于单字节的符号,字节的第一位设为0,前面7位为这个符号的 Unicode 码。因而对于英语字母,UTF-8 编码和 ASCII 码是雷同的。 2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,前面字节的前两位一律设为10。剩下的没有提及的二进制位,全副为这个符号的 Unicode 码。 ...

October 15, 2021 · 1 min · jiezi

关于字符集:字符集和字符编码什么关系

严格来说,字符集和字符编码不是一个概念:字符集定义了字符和二进制的对应关系,为每个字符调配了惟一的编号。能够将字符集了解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。 字符编码规定了如何将字符的编号存储到计算机中。如果应用了相似 GB2312 和 GBK 的变长存储计划(不同的字符占用的字节数不一样),那么为了辨别一个字符到底应用了几个字节,就不能将字符的编号间接存储到计算机中,字符编号在存储之前必须要通过转换,在读取时还要再逆向转换一次,这套转换计划就叫做字符编码。 有的字符集在制订时就思考到了编码的问题,是和编码联合在一起的,例如ASCII、GB2312、GBK、BIG5 等,所以无论称作字符集还是字符编码都无所谓,也不好辨别两者的概念。而有的字符集只管制订字符的编号,至于怎么存储,那是字符编码的事件,Unicode 就是一个典型的例子,它只是定义了寰球文字的惟一编号,咱们还须要 UTF-8、UTF-16、UTF-32 这几种编码方案将 Unicode 存储到计算机中。 Unicode 能够应用的编码方案有三种,别离是: UTF-8:一种变长的编码方案,应用 1~6 个字节来存储;UTF-32:一种固定长度的编码方案,不论字符编号大小,始终应用 4 个字节来存储;UTF-16:介于 UTF-8 和 UTF-32 之间,应用 2 个或者 4 个字节来存储,长度既固定又可变。UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格局”,前面的数字表明至多应用多少个比特位(Bit)来存储字符。 1) UTF-8UTF-8 的编码规定很简略: 如果只有一个字节,那么最高的比特位为 0,这样能够兼容 ASCII;如果有多个字节,那么第一个字节从最高位开始,间断有几个比特位的值为 1,就应用几个字节编码,剩下的字节均以 10 结尾。具体的表现形式为: 0xxxxxxx:单字节编码模式,这和 ASCII 编码齐全一样,因而 UTF-8 是兼容 ASCII 的;110xxxxx 10xxxxxx:双字节编码模式(第一个字节有两个间断的 1);1110xxxx 10xxxxxx 10xxxxxx:三字节编码模式(第一个字节有三个间断的 1);11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码模式(第一个字节有四个间断的 1)。xxx 就用来存储 Unicode 中的字符编号。 上面是一些字符的 UTF-8 编码实例(着色局部示意原本的 Unicode 编号): 字符字母N符号æ中文⻬Unicode 编号(二进制)010011101110011000101110 11101100Unicode 编号(十六进制)4EE62E ECUTF-8 编码(二进制)0100111011000011 1010011011100010 10111011 10101100UTF-8 编码(十六进制)4EC3 A6E2 BB AC对于罕用的字符,它的 Unicode 编号范畴是 0 ~ FFFF,用 1~3 个字节足以存储,只有及其常见,或者只有多数地区应用的字符才须要 4~6个字节存储。 ...

May 14, 2021 · 2 min · jiezi

关于字符集:自动根据文本文件的字符集编码加载文件内容字符串

主动依据文本文件的字符集编码加载文件内容字符串,并反对按原始编码格局再次写入。 using System;using System.IO;using System.Linq;using System.Text;/// <summary> /// 获取文件的编码格局 /// </summary> public class TextEncode{ public Encoding Encoding { get; private set; } public byte[] BOM { get; private set; } public string ReadText(string fileName, out Encoding enc) { var r = ReadText(fileName); enc = Encoding; return r; } public string ReadText(string fileName) { Encoding encoding = null; byte[] bytes = File.ReadAllBytes(fileName); int bomLen = 0; if (bytes.Length > 1) { if (bytes[0] == 0xFE && bytes[1] == 0xFF) //UTF-16(大端序) { encoding = new UnicodeEncoding(true, true); bomLen = 2; } else if (bytes[0] == 0xFF && bytes[1] == 0xFE) //UTF-16(小端序) { encoding = new UnicodeEncoding(false, true); bomLen = 2; } } if (encoding == null && bytes.Length > 2) { if ((bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)) //UTF-8 { encoding = new UTF8Encoding(true); bomLen = 3; } } if (encoding == null && bytes.Length > 3) { if (bytes[0] == 0x00 && bytes[1] == 0x00 && bytes[2] == 0xFE && bytes[3] == 0xFF) //UTF-32(大端序) { encoding = new UTF32Encoding(true, true); bomLen = 4; } else if (bytes[0] == 0xFF && bytes[1] == 0xFE && bytes[2] == 0x00 && bytes[3] == 0x00) //UTF-32(小端序) { encoding = new UTF32Encoding(false, true); bomLen = 4; } } if (encoding == null && IsUTF8Bytes(bytes)) { encoding = Encoding.UTF8; //UTF8无BOM } if (encoding == null) encoding = Encoding.Default; Encoding = encoding; BOM = new byte[bomLen]; Array.Copy(bytes, BOM, bomLen); return encoding.GetString(bytes, bomLen, bytes.Length - bomLen); } /// <summary> /// 判断是否是不带 BOM 的 UTF8 格局 /// </summary> /// <param name="data"></param> /// <returns></returns> private bool IsUTF8Bytes(byte[] data) { int charByteCounter = 1; //计算以后正剖析的字符应还有的字节数 byte curByte; //以后剖析的字节. for (int i = 0; i < data.Length; i++) { curByte = data[i]; if (curByte == 0) throw new FormatException("非预期的byte格局"); if (charByteCounter == 1) { if (curByte >= 0x80) { //判断以后 while (((curByte <<= 1) & 0x80) != 0) { charByteCounter++; } //标记位首位若为非0 则至多以2个1开始 如:110XXXXX...........1111110X if (charByteCounter == 1 || charByteCounter > 6) { return false; } } } else { //若是UTF-8 此时第一位必须为1 if ((curByte & 0xC0) != 0x80) { return false; } charByteCounter--; } } if (charByteCounter > 1) { throw new FormatException("非预期的byte格局"); } return true; } /// <summary> /// 已雷同的Encoding和BOM再次写入 /// </summary> public void WriteBySameEncoding(string fileName, string content) { if (BOM == null) { throw new Exception("须要先调用ReadText办法"); } using (var file = File.Create(fileName)) { file.Write(BOM, 0, BOM.Length); var bytes = Encoding.GetBytes(content); file.Write(bytes, 0, bytes.Length); } }}

September 21, 2020 · 2 min · jiezi