简体汉字编码中区位码、国标码、内码、外码、字形码的区别及关系

在理解2NF上有必要先认识到在关系数据库中完全函数依赖、部分函数依赖、候选码、主属性和非主属性的概念。
完全函数依赖:在关系模式R(U)中,如果存在X->Y(X里的属性可以一个或一组),而X属性里的任何一个真子集x’,都有x’-->Y(不能决定Y),则Y对X完全函数依赖。
部分函数依赖:在关系模式R(U)中,如果存在X->Y,Y不完全依赖于X(X的属性中有一个或一组不存在也不影响决定Y),则称Y对X部分函数依赖。
候选码:在关系模式R(U)中,设K为R(U)的属性或属性组,若U对K完全函数依赖,则K为关系模型R(U)的候选码(函数依赖的概念定义)
还有一种解释为:若关系中的一个属性或属性组的值能够唯一地标识一个元组,且他的真子集不能唯一的标识一个元组,则称这个属性或属性组做候选码。
主属性:在关系中任何一个候选码所包含的属性都称为主属性–而一个关系中的候选码可以有多个,所以说主属性一定是所有候选码的属性的并集,例如属性(A,B,C,D,E)中,A,B,C都是候选码,那么主属性为A,B,C。
非主属性:在关系模式R(U)中,不包含在任何候选码中的属性。
讲解完这些概念后,我们来看2范式的定义。
定义:若关系模式R∈1NF(属性不能再分割),且每一个非主属性完全函数依赖于任何一个候选码,则R∈2NF。
重点在于分析“且每一个非主属性完全函数依赖于任何一个候选码”这句话,这句话就是在说:你要判断这个属于1NF的关系是不是属于2NF,首先你得求出这个关系所有的候选码,从所有的候选码中求出主属性,再去求解非主属性与每一个候选码是否是完全函数依赖,如果存在非主属性与其中一个候选码存在部分函数依赖,则它不属于2NF。
举个含有多个候选码的例子:
关系模式R(U,F),U={A,B,C,D,E},F={A->B,A->C,C->D,BCD->E,E->D,E->A},问该模式属于第几范式?
解:
首先,该模式满足1NF
根据F求解出候选码:(A),(E),(BC)
则:
主属性为:A,B,C,E
非主属性为:D
分析:
候选码(A):D完全依赖于候选码(A)
候选码(E):D完全依赖于候选码(E)
候选码(BC):从F中可以推导出BC->D,并且C->D,此时存在非主属性D完全函数依赖于候选码(BC)属性里的真子集C,导致非主属性D部分函数依赖于候选码(BC)
因此,此关系模式不属于2NF,仅仅属于1NF
所谓关系键,指的是一个表中的一个(或一组)属性,用来标识该表的每一行或与另一个表产生联系。
话不多说,上图:
相信这个图已经画得很清晰了,下面逐一解释:
1、码=超键:能够唯一标识一条记录的属性或属性集。
2、候选键=候选码:能够唯一标识一条记录的最小属性集
3、主键=主码:某个能够唯一标识一条记录的最小属性集(是从候选码里人为挑选的一条)
4、主属性 包含在任一候选码中的属性称主属性。简单来说,主属性是候选码所有属性的并集
非主属性 不包含在候选码中的属性称为非主属性。 非主属性是相对于主属性来定义的。
5、外键(foreign key):子数据表中出现的父数据表的主键,称为子数据表的外键。
6、全码:当所有的属性共同构成一个候选码时,这时该候选码为全码。(教师,课程,学生)假如一个教师可以讲授多门课程,某门课程可以有多个教师讲授,学生可以听不同教师讲授的不同课程,那么,要区分关系中的每一个元组,这个关系模式R的候选码应为全部属性构成 (教师、课程、学生),即主码。
7、代理键:当不适合用任何一个候选键作为主键时(如数据太长等),添加一个没有实际意义的键作为主键,这个键就是代理键。(如常用的序号1、2、3)
8、自然键:自然生活中唯一能够标识一条记录的键(如身份证)
下面举例说明
课本上的定义过于笼统,下面我用一张学生成绩信息表给大家详细的说一下:
学生成绩信息表中有(学号、姓名、性别、年龄、系别、专业等)
超键/码:
由于学号能确定一个学生,因此学生表中含有学号的任意组合都为此表的超键。如:(学号)、(学号,姓名)、(学号,性别)等
若我们假设学生的姓名唯一,没有重名的现象。
学号唯一,所以是一个超键
姓名唯一,所以是一个超键
(姓名,性别)唯一,所以是一个超键
(姓名,年龄)唯一,所以是一个超键
(姓名,性别,年龄)唯一,所以是一个超键
候选键:
学号唯一,而且没有多余属性,所以是一个候选键
姓名唯一,而且没有多余属性,所以是一个候选键
(姓名,性别)唯一,但是单独姓名一个属性就能确定这个人是谁,所以性别这个属性就是多余属性,所以(姓名,性别)不是候选键
(姓名,年龄),(姓名,性别,年龄)同上,也不是候选键
主键:
主键就是候选键里面的一个,是人为规定的,例如学生表中,我们通常会让“学号”做主键,学号能唯一标识这一个元组。
外键:
外键就很简单了,假如我们还有一个教师表,每个教师都有自己的编号,假设老师编号在教师表中是主键,在学生表中它就是外键。
做一道练习题巩固一下(假设名字可能有重复):
参考答案:
1.答案:
(1)候选关键字2个:(学号),(身份证号)
(2)主关键字:(学号)
(3)主属性2个:学号,身份证号
(4)非主属性2个:姓名,系别
2. 答案:
(1)候选关键字1个:(学号,课程号)
(2)主关键字:(学号,课程号)
(3)主属性2个:学号,课程号
(4)非主属性1个:成绩
(5)外部关键字:学号(引用学生信息表中的学号),课程号(引用课程信息表中的课程号)
3.答案:
(1)候选关键字1个:(课程号)
(2)主关键字:(课程号)
(3)主属性1个:课程号
(4)非主属性2个:课程名,学分
如有疑问请在评论区留言,如有帮助麻烦右上角点个赞~~蟹蟹(*^▽^*)
首先声明这篇博客的创作过程,大部分文本来自“笨笨阿林”的原创文章。我在看完后加入了部分自己的理解,有些地方做了略微调整,比如将有些不易理解的地方重新解释,多余的话删除等;也在最后加入了一个例子来证实了一下在GB2312编码下从区位码到实际在计算机中存储的二进制字节流的转换过程。因此我将这篇文章定为原创,望理解。
下面是正文。
GB2312等GB类汉字编码方案的具体实现方式是怎样的?区位码是什么?国标码是什么?内码、外码、字形码又是什么意思?它们是如何转换的,又为什么要这样转换?
下面以GB2312为例来加以说明(由于GBK、GB18030是以GB2312为基础扩展而来,因此编码实现方式与GB2312一样)。
一、区位码
整个GB2312字符集分成94个区,每区有94个位,每个区位上只有一个字符,即每区含有94个汉字或符号,用所在的区和位来对字符进行编码(实际上就是字符编号、码点编号),因此称为区位码。
换言之,GB2312将包括汉字在内的所有字符编入一个94 * 94的二维表,行就是“区”、列就是“位”,每个字符由区、位唯一定位,其对应的区、位编号合并就是区位码。比如“中”字
在54区48位,所以“中”字的区位码是:5448(注意,GB类汉字编码为双字节编码,因此,45相当于高位字节,82相当于低位字节)。
(笨笨阿林原创文章,转载请注明出处)
二、国标码(交换码)
虽然GB2312为中文编码,我们也要使用到英文字母等字符,况且当时ASCII已经通用,所以要使GB2312能够兼容ASCII才行。so,为了兼容,GB2312在设计时避开了ASCII字符中的不可显示字符0000 0000 ~ 0001 1111(十六进制为0 ~ 1F,十进制为0 ~ 31)及空格字符0010 0000(十六进制为20,十进制为32)(为什么只避开ASCII中0~32的不可显示字符和空格字符,不避开其他字符呢?后面解释),国标码(又称为交换码)规定表示汉字的范围为(0010 0001,0010 0001) ~(0111 1110,0111 1110),十六进制为(21,21) ~ (7E,7E),十进制为(33,33) ~ (126,126)。因此,必须将“区码”和“位码”分别加上(注意,一定是分别加上,因为区码和位码分别代表一个字节)20H(十六进制数,代表十进制中的32,H为十六进制数的后缀:Hexadecimal),作为国标码。也就是说,国标码相当于将区位码向后偏移了32,以避免与ASCII字符中0~32的不可显示字符和空格字符相冲突。例如“中”字的区位码为5448,转为国标码的过程为,区码:(54+32)=86,位码:(48+32)=80,所以“中”字的国标码表示为:(86, 80)。十六进制为(56H, 50H)。
三、内码(机内码)
国标码还不能直接在计算机上使用,因为这样还是会和ASCII中的除控制字符外的其他字符冲突(冲突的结果就是导致乱码)。总结一下:
从区位码(国家标准定义) ---> 区码和位码分别+32(即+20H)得到国标码 ---> 再分别+128(即+80H)得到机内码(与ACSII码不再冲突)。
四、为什么要加上20H和80H?
首先谈谈为什么要加上20H:(笨笨阿林原创文章,转载请注明出处)
五、外码(输入码、输入法编码)
1.外码也叫输入码、输入法编码,是用来将汉字输入到计算机中的一组键盘符号,是作为汉字输入用的编码。3)字形编码,比如五笔、表形码、郑码等。
六、字形码(字型码、字模码、输出码)
1.字形码,又称为字型码、字模码、输出码,属于点阵代码的一种。为了将汉字在显示器或打印机上输出,把汉字按图形符号设计成点阵图,就得到了相应的点阵代码(字形码)。也就是用0、1表示汉字的字形,将汉字放入n行*n列的正方形(点阵)内,该正方形共有n^2个小方格,每个小方格用一位二进制表示,凡是笔划经过的方格值为1,未经过的值为0。
2.显示一个汉字一般采用16×16点阵或24×24点阵或48×48点阵。已知汉字点阵的大小,可以计算出存储一个汉字所需占用的字节空间。因此,字节数=点阵行数×(点阵列数/8)。
3.为了将汉字的字形显示输出或打印输出,汉字信息处理系统还需要配有汉字字形库,也称字模库,简称字库,它集中了汉字的字形信息。
七、小结
可以这样理解,为了在计算机内表示汉字而采取统一的编码方式所形成的汉字编码叫内码。为方便汉字输入而形成的汉字编码为外码,也叫输入码。为显示输出和打印输出汉字而形成的汉字编码为字形码,也称为字模码、输出码。
计算机通过键盘输入的外码(重码时还需附加选择编号)对应于汉字内码,将汉字外码转换(即映射)为汉字内码,以实现输入汉字的目的;通过汉字内码在字模库(即字库)中找出汉字的字形码,将汉字内码转换(即映射)为汉字字形码,以实现显示输出和打印输出汉字的目的。(笨笨阿林原创文章,转载请注明出处)
八、实例证明区位码与内码之间的转换关系是成立的
这个方法的作用是:使用参数指定的字符集,来编码这个String,称为一个字节序列,并把结果保存在一个字节数组中。
这里我们使用GB2312来编码“中”字,输出后得到结果:
为什么是负数呢?因为java中对数字的保存采用补码的形式(不了解补码的请看我的这篇博客)。将负数转换为正数为(214, 208)。将其转换为国标码为(214-128,208-128),也就是(86,80).再将国标码转换为区位码:(86-32, 80-32),(54,48)。
现在来查看一下“中”字的区位码:
下面以GB2312为例来加以说明(由于GBK、GB18030是以GB2312为基础扩展而来,因此编码实现方式与GB2312一样)。
一、区位码
1.
整个GB2312字符集分成94个区,每区有94个位,每个区位上只有一个字符,即每区含有94个汉字或符号,用所在的区和位来对字符进行编码(实际上就是字符编号、码点编号),因此称为区位码(或许叫“区位号”更为恰当)。
换言之,GB2312将包括汉字在内的所有字符编入一个94 * 94的二维表,行就是“区”、列就是“位”,每个字符由区、位唯一定位,其对应的区、位编号合并就是区位码。比如“万”字在45区82位,所以“万”字的区位码是:45 82(注意,GB类汉字编码为双字节编码,因此,45相当于高位字节,82相当于低位字节)。
2.
GB2312字符集中:
1)01~09区(682个):特殊符号、数字、英文字符、制表符等,包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母等在内的682个全角字符;
2)10~15区:空区,留待扩展;
3)16~55区(3755个):常用汉字(也称一级汉字),按拼音排序;
4)56~87区(3008个):非常用汉字(也称二级汉字),按部首/笔画排序;
5)88~94区:空区,留待扩展。
二、国标码(交换码)
1.
为了避开ASCII字符中的不可显示字符0000 0000 ~ 0001 1111(十六进制为0 ~ 1F,十进制为0 ~ 31)及空格字符0010 0000(十六进制为20,十进制为32)(至于为什么要避开、又为什么只避开ASCII中0~32的不可显示字符和空格字符,后文有解释),国标码(又称为交换码)规定表示汉字的范围为(0010 0001,0010 0001) ~ (0111 1110,0111 1110),十六进制为(21,21) ~ (7E,7E),十进制为(33,33) ~ (126,126)(注意,GB类汉字编码为双字节编码)。
因此,必须将“区码”和“位码”分别加上32(十六进制为20H,后缀H表示十六进制),作为国标码。也就是说,国标码相当于将区位码向后偏移了32,以避免与ASCII字符中0~32的不可显示字符和空格字符相冲突。
2.
注意,国标码中是分别将区位码中的“区”和“位”各自加上32(20H)的,因为GB2312是DBCS双字节字符集,国标码属于双字节码,“区”和“位”各作为一个单独的字节。
这样我们可以算出“万”字的国标码十进制为:(45+32,82+32) = (77,114),十六进制为:(4D,72H),二进制为:(0100 1101,0111 0010)。
(笨笨阿林原创文章,转载请注明出处)
三、内码(机内码)
1.
不过国标码还不能直接在计算机上使用,因为这样还是会和早已通用的ASCII码冲突(导致乱码)。
比如,“万”字国标码中的高位字节77与ASCII的“M”冲突,低位字节114与ASCII的“r”冲突。因此,为避免与ASCII码冲突,规定国标码中的每个字节的最高位都从0换成1,即相当于每个字节都再加上128(十六进制为80,即80H;二进制为1000 0000),从而得到国标码的“机内码”表示,简称“内码”。
2.
由于ASCII码只用了一个字节中的低7位,所以,这个首位(最高位)上的“1”就可以作为识别汉字编码的标志,计算机在处理到首位是“1”的编码时就把它理解为汉字,在处理到首位是“0”的编码时就把它理解为ASCII字符。
比如:
77 + 128 = 205(二进制为1100 1101,十六进制为CD)
114+ 128 = 242(二进制为1111 0010,十六进制为F2)
3.
我们可以来检验一下。打开记事本输入“万”字,编码选择为ANSI(Windows记事本中的ANSI编码对于简体汉字而言就是GB类编码,详见后文解释),保存,如下图所示。
然后用二进制编辑器(比如UltraEdit)打开刚才保存的文件,切换到十六进制模式,会看到:CD F2,这就是“万”字的内码,如下图所示。
总结一下:
从区位码(国家标准定义) ---> 区码和位码分别+32(即+20H)得到国标码 ---> 再分+128(即+80H)得到机内码(与ACSII码不再冲突)。
因此,区位码的区和位分别+160(即+A0H,32+128=160)可直接得到内码。用十六进制表示就是:
区位码(区码,位码) + (20H,20H) + (80H,80H) =区位码(区码,位码) + (A0H,A0H) = 内码(高字节,低字节)。
四、为什么要加上20H和80H?
1.
区位码、国标码、内码的转换非常简单,但是令人迷惑的是为什么要这么转换?
首先,需要注意到一点,GB2312虽说是对中文编码,但是里面也有对26个英文字母和一些特殊符号的编码,按理说这些和ASCII重合的字符(33~127)应该无需再重新编码,直接沿用ASCII中的不就行了?
2.
原来,当时在制定GB2312时,决定对ASCII中的可打印字符,也就是英文字母、数字和符号部分(33~126,127为不可打印的DEL)重新编入GB2312中,以两个字节表示,称之为全角字符(全角字符在屏幕上的显示宽度为ASCII字符的两倍,后来也因此而将对应的ASCII字符称之为半角字符)。
而对于ASCII中前32个不可显示也不可打印的控制字符(ASCII码为0~31),以及第33个可显示但不可打印的空格字符(ASCII码为32)等共33个不可打印字符的编码则直接沿用,不再重新编码。
3.
因为要保留这33个不可打印字符,就不能直接采用区位码作为计算机直接处理的机内码,需要将区位码向后偏移32以避开冲突(为什么是偏移32,而不是偏移33?因为区位码中的区码和位码都是从1开始计数的,不像ASCII码是从0开始计数的)。
十进制数字32的十六进制表示就是20(为区别于十进制,记作20H),这也就是区位码要加上20H(区码和位码各自加上20H)才能得到国标码的原因。
(笨笨阿林原创文章,转载请注明出处)
3.
很显然,如果直接采用国标码作为计算机直接处理的机内码的话,还将会产生另一个弊端,即用ASCII码编码的英文字符在GB2312编码环境中无法打开,一打开就会乱码。
因为国标码虽然相较于区位码避开了ASCII码中0~32的前33个不可打印字符,但并没有避开ASCII码中的英文字母、数字和符号(33~126,共94个字符,127为不可打印的DEL)等可打印字符。也就是说,国标码并不是完全兼容ASCII码的。
4.
为了解决这个弊端,考虑到ASCII码只使用了一个字节中的低7位,最高位(即首位)为0,于是决定将国标码每个字节的最高位设为1(国标码的两个字节中的最高位都恒为0,即国标码中的每个字节实际上也只用了一个字节中的低7位),这就是GB2312的机内码(即内码),简称GB2312码。
这样一来就彻底区分开了ASCII码和GB2312码。这也是为什么国标码还要加上(80H,80H)才能得到机内码的原因。
5.
看到这里,有人或许又要问了:如果仅仅是为了避免与ASCII编码相冲突,为什么最初不直接将区位码的区码和位码的最高位从0改为1(相当于各自直接加上128),这样不就无需经过国标码多此一举的中间转换了吗?而且还无需后移32,也就不用浪费这部分编码空间。
对此本人也很困惑,在网上搜了很久也没找到答案,因此具体原因不得而知。或许是一开始考虑不周?或许为了未来扩展所需而预留一部分空间?又或许是有其他不得已的原因?有知道的朋友还望能指点迷津。
五、外码(输入码、输入法编码)
1.
外码也叫输入码、输入法编码,是用来将汉字输入到计算机中的一组键盘符号,是作为汉字输入用的编码。
英文字母只有26个,可以把所有的字符都放到键盘上,而使用这种办法把所有的汉字都放到键盘上,是不可能的。所以汉字系统需要有自己的输入码体系,使汉字与键盘能建立对应关系。
2.
目前常用的外码分为以下几类:
1)数字编码,比如区位码;
2)拼音编码,比如全拼、双拼、自然码等;
3)字形编码,比如五笔、表形码、郑码等。
六、字形码(字型码、字模码、输出码)
1.
字形码,又称为字型码、字模码、输出码,属于点阵代码的一种。
为了将汉字在显示器或打印机上输出,把汉字按图形符号设计成点阵图,就得到了相应的点阵代码(字形码)。
也就是用0、1表示汉字的字形,将汉字放入n行*n列的正方形(点阵)内,该正方形共有n^2个小方格,每个小方格用一位二进制表示,凡是笔划经过的方格值为1,未经过的值为0。
2.
显示一个汉字一般采用16×16点阵或24×24点阵或48×48点阵。已知汉字点阵的大小,可以计算出存储一个汉字所需占用的字节空间。
比如,用16×16点阵表示一个汉字,就是将每个汉字用16行,每行16个点表示,一个点需要1位二进制代码,16个点需用16位二进制代码(即2个字节),共16行,所以需要16行×2字节/行=32字节,即16×16点阵表示一个汉字,字形码需用32字节。
因此,字节数=点阵行数×(点阵列数/8)。
3.
显然,字形码所表示的字符,相对于抽象字符表ACR里的“抽象”字符,可称之为“具体”字符,因为具有了“具体”的外形。
为了将汉字的字形显示输出或打印输出,汉字信息处理系统还需要配有汉字字形库,也称字模库,简称字库,它集中了汉字的字形信息。
字库按输出方式可分为显示字库和打印字库。用于显示输出的字库叫显示字库,工作时需调入内存。用于打印输出的字库叫打印字库,工作时无需调入内存。
字库按存储方式也可分为软字库和硬字库。软字库以文件的形式存放在硬盘上,现多用这种方式。硬字库则将字库固化在一个单独的存储芯片中,再和其它必要的器件组成接口卡,插接在计算机上,通常称为汉卡。这种方式现已淘汰。
七、小结
计算机通过键盘输入的外码(重码时还需附加选择编号)对应于汉字内码,将汉字外码转换(即映射)为汉字内码,以实现输入汉字的目的;通过汉字内码在字模库(即字库)中找出汉字的字形码,将汉字内码转换(即映射)为汉字字形码,以实现显示输出和打印输出汉字的目的。
(笨笨阿林原创文章,转载请注明出处)
【预告:下一篇将重点剖析非常容易令人困惑的所谓ANSI编码与代码页(Code Page),敬请关注!】
<div id="blog_post_info">
« 上一篇: 刨根究底字符编码之五——简体汉字编码方案(GB2312、GBK、GB18030、GB13000)以及全角、半角、CJK
» 下一篇: 刨根究底字符编码之七——ANSI编码与代码页(Code Page)