查看原文
其他

GUI必备知识之“告别”乱码(浅显易懂)

bug菌 最后一个bug 2021-01-31

1、简单聊一聊

    今天文章开头为大家推荐一首《灌篮高手》主题曲,每次听到这首歌都会让人充满斗志,这部动漫也是作者小时候的最爱,每次放学都会和邻居小伙伴一起去看,甚至曾经因为它苦练篮球,不过最后还是被拉回了现实。

    现在想想,可能小时候都会比现在的自己勇敢,勇敢的去追求自己想要的,自己热爱的,而当渐渐长大被现实磨平了棱角,身上背负的越多,就开始了畏惧,惧怕自己能力,惧怕别人的眼光等等

    好了,今天为大家带来GUI字符显示的相关知识,前面作者写了一篇《【典藏】自制小型GUI界面框架(设计思想篇)》,主要是用于大家平时自己设计一些小型GUI的思路和框架,后续GUI部分会慢慢的分析一些具体的实现原理,大家明白了这些原理以后去研究一下目前开源的GUI界面会发现其实都大同小异。

2、GUI字符显示

    在前面作者的文章中提到过我们GUI显示的基础都是像素点的绘制,由点到线、由线到面;那么GUI中显示的字符都可以看成一个个的面,说得通俗一点就是“贴图”,我们先把一个个的字符图片以数据的形式存储下来,当我们需要显示的时候调取相应的图片进行绘制在屏幕上即可,通常我们叫这样的图片叫做字模,一堆这样的子模就组成了我们的字库

    从上图我们可以了解到从字符串到我们的字库,从字库到LCD显示屏都是一种映射关系,我们只需要规定了这种关系,并且严格按照这种关系执行解析对应的字符串就可以成功显示到我们的LCD屏幕上,然而当我们经常看到各种各样的乱码,主要的原因就是没有遵循对应的关系。为了统一规范这种字符的映射关系便出现了字符编码。


3、字符编码

1)何为字符编码? 

    大家应该最熟悉的编码是ASCII编码,ASCII全称 : American Standard Code for Information Interchange,英文翻译过来就是 : 美国信息交换标准代码,该编码一共是128个字符,所谓编码可以说就是为每个字符规定一个唯一的标识ID(为了便于索引到字符资源),就像我们第一小节中的Bug三个字符通过ASCII码表可以得到其编码分别为:

    当我们用ASCII文本编辑,通过编译链接生成可执行文件以后,对应的字符串通过编码如字符“B”,对应的0x42这个16进制ID,然后在字库中找到对应的字模,最后把字模显示在LCD显示屏上。

    由于最开始计算机是美国人发明的,那ASCII编码肯定是能够满足他们自身的需求,可是随着计算机的普及,各个国家地区的语言等等存在差异,那这128个字符就远远不够了,像我们博大精深的汉字,就需要额外对汉字字符进行编码了,相应的编码有 : 国标GB2312、GBK等等,不过不同国家或者区域所扩展的字符集都会尽量对ASCII并进行兼容,这也是为什么采用ASCII编码进行编辑大多数情况不会有乱码问题的原因。


2)编码、解码过程 

    我们平时编辑好的本文文件,都是以数据形式存储在存储器中的(因为计算机不能直接处理文件,文件都是以数据的方式进行存储的),只是说被文件系统管控着,我们用文本工具打开对应的文件就是以文本工具确定的编码方式去解码文本数据,然后通过GUI展示给读者;同样我们编译好文本文件进行保存,文本工具以编辑器设定的编码类型对文字进行编码并进行存储。


    同样编译生成对应的可执行文件里面的字符数据也都是存在相应的编码。当我们需要使用到这些字符,或者说呈现这些字符的时候就涉及到一个解码的过程,可以一种编码方法,就会对应着一套解码算法(如果我们采用A进行编码并存储,然后采用B解码方式打开并显示,如果AB不兼容就会存在最后显示乱码的现象),比如说我们的ASCII码只有128个字符,那么用1个byte都可以对所有字符进行编码,那么我们在进行解码的时候只需要一个byte一个byte进行解析便能够获得当初进行编码的字符了。

    对于数量庞大汉字,用一个byte进行编码无法满足要求,那我们就使用两个byte,不过为了兼容ASCII,就不能说单纯的把两个byte进行一一对应就可以的,这就涉及到一个解码的过程的需求了。

    我们假定GB2312都是采用两个字节解码一个纯粹的英文文本,那么每次读取两个byte获得对应编码然后在字库中查找并且能够正常显示,当我们用ASCII文本打开,这个时候1个byte1个byte的解码,解码出来的字符会是原来的两倍,然而在平时的使用过程中并没有出现这样的现象,那原因是什么呢?

    答案是 : GB2312编码并不是全部用两个byte来兼容ASCII,其存在单字节编码,ASCII的编码都是<0x80即二进制为:1000,0000,那么我们通过判断最高位,如果为1表示该部分为非ASCII,然后连同后面的一个字节一同读取并进行解码,如果为0则单独读取这个byte进行ASCII编码,这样就完全兼容ASCII了,所以这就是为什么平时建议大家写程序能用英文尽量用英文,同样其他的编码和解码方式都大同小异了。

3)一统江山的编码-Unicode 

    各个国家或者区域有着各自的编码方式,但是为了使得字符显示能够拥有更好的兼容性等需求,这就需要有一个非常大的编码集合来容纳并统一这所有的字符,于是便诞生了Unicode编码,Unicode仅仅只是固定了每个字符对应的ID是多少,并没有定义这些ID该怎样存储怎样解码,像我们前面说的GB2312既规定了中文的ID,也规定了ASCII与非ASCII的存储区别和解码过程,而Unicode仅仅只是规定了每个字符的ID,至于如何存储如何解码就涉及到了UTF-8,UTF-16,UTF-32等等了。


    根据上面的图,我们暂且叫UTF-8,UTF-16,UTF-32等为Unicode存储格式,根据其后数据8,16,32分别表示他们的最小存储单元,如果少了就增加一个最小单元,比如UTF-8编码ASCII就只需要1byte,如果表示其他编码根据Unicode可能就需要2byte等等,同样UTF-16也是一样的,最小存储单元为2byte,如果2byte少了就需要4个byte来进行存储。

    从UTF-8,UTF-16,UTF-32这几种存储方式来看,仅仅只有UTF-8能够兼容ASCII,因为其满足单字节编码

    既然每种编码都形成了一一对应的映射关系,并且有着唯一的标识,这样编码之间就可以进行相互的转换,在我们进行桌面应用程序中,经常会涉及到对应的编码的转化,于是便可以查找对应的转化函数或者方法来完成编码转化,一般做法是通过Unicode作为桥梁来进行转化。


4、最后小结

     最近确实忙醉了,这篇文章也是每天晚上下班抽出额外的时间为大家赶制的,希望能够给大家带来一些新的收获或者启发。大家都好好加油,fighting!

    好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地。同时非常感谢各位小伙伴的支持,我们下期精彩见!

推荐好文  点击蓝色字体即可跳转

【连载】重温C++之“重载”(第三篇)

【经典】解析一个STM32在线升级实例(usart版本)

【典藏】深度剖析单片机程序的运行(C程序版) 

【重磅】剖析MCU的IAP升级软件设计(设计思路篇)

☞ C语言为什么一般不在.h中定义函数或者变量?(精华)

手把手教你写Modbus-RTU协议(理论篇)

【典藏】大佬们都在用的结构体进阶小技巧

听说因为代码没"对齐"程序就奔了?(深度剖析)

【典藏】自制小型GUI界面框架(设计思想篇)

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存