unicode ucs2 utf16 utf8 ansi GBK GB2312 互转 及 渲染

我们常见的字符编码有ascii、utf8、utf16、utf32、GB2312、GBK 、GB18080有一个共同的称呼,机内码,这些字符编码方式被广泛应用于计算机中的文件中、网络传输、运行时内存中的字符表示等等。

qt vs 编程 字符编码 程序从源码到编译到显示过程中存在的字符编码及转换-CSDN博客

一、ansi编码 与 GBK、GB2312、ASCII关系

像ASCII一样,GB2312和GBK既包含字符集,又包含编码。GBK字符集编码 兼容 GB2312字符集编码,GB2312字符编码 兼容 一个字节的ASCII字符集编码,像俄罗斯套娃一样。所以GB2312编码和GBK编码 是一个字节(ASCII码)或两个字节的编码。为了囊括更多字符(比如中国各个少数名族的语言及其字符),出现了一个或两个或四个字节的,且兼容GBK字符集编码的GB18030字符集编码。

举个例子: 假如你使用GB2312写了这么一句话: 我叫ABC
它的二进制编码是这样的:11001110 11010010 10111101 11010000 01000001 01000002 01000003
解码的时候,当遇到1开头的字节,就把两个字节合起来解释为一个字符,于是11001110 11010010会被解释为我;遇到0开头的字节,就只把这个字节解释为一个字符,于是01000001就会被解释为A了。
所以,假设你用GB2312写了一段英文ABC
它的二进制编码会是01000001 01000002 01000003
可以发现,如果使用ASCII写的话,编码也是一样的。
这就能解释了,为什么用GB2312编码的英文,可以用ASCII正常解码出来。

再举个例子:

AA你好 是GB2312字符集中的字符,对应的GB2312编码为0x41 0x41 0xC4E3 0xBAC3
GBK兼容GB2312,所以 AA你好 的GBK编码为:0x41 0x41 0xC4E3 0xBAC3

char *ptr=“AA你好” //在vs编译后使用ansi编码,内存中对应的值为:
41 41 C4 E3 BA C3
汉字字符集编码查询;中文字符集编码:GB2312、BIG5、GBK、GB18030、Unicode

字符 编码/解码 - 在线工具 
GB2312汉字编码字符集对照表BeJSON.com

ansi编码方案_百度百科 不是指一种具体的编码方案,它是跟国家相关的,每个国家都有字节本地化的一套字符集和编码。ansi采用一个或两个字节编码字符,其意义更像是强调本地化编码方案要与ASCII兼容的规范。在中国,ansi编码默认直接采用GBK字符集编码,也就是说ansi编码与GBK编码是一样的,GBK中包含了中国常用的字符集;在日本,ansi则采用另外一种字符集编码(因为日文字符集与中文字符集不一样),在其他地方亦如此,美个国家或地区都有自己的字符集及对应的编码,但基本都存在类似GBK这样的字符集编码,用一到两个字节就表示完常用的字符集。字节本地化的意义在于节省内容存储空间,方便本地的数据传输,但是却给跨区域数据传输造成不便。

ASCII字符集中有128个字符。ASCII码对照表-BeJSON.com 
GB2312中收录了6763个汉字以及682个特殊符号,已经囊括了生活中最常用的所有汉字及所兼容的ASCII字符集。GB2312汉字编码字符集对照表BeJSON.com  
GBK表示的汉字达到了20902个,另有984个汉语标点符号、部首等,包含了 繁体字 及所兼容的GB2312字符集。最全面的GBK编码表/GBK字符集 - 常用参考表对照表 
GB18030-2000规定了常用非汉字符号和27533个汉字(包括部首、部件等)的编码。
GB18030-2005是以汉字为主并包含多种我国少数民族文字的超大型中文编码字符集,其中收入汉字70000余个,在GB18030-2000的基础上增加了42711个汉字和多种我国少数民族文字的编码(如藏、蒙古、傣、彝、朝鲜、维吾尔文等)。
GB18030-2022在2005版基础上再增加了一万多个汉字,使得汉字总数达到87887个,全面覆盖了《通用规范汉字表》中的汉字。收录的少数民族文字包括:藏文、滇东北苗文、彝文、傈僳文、朝鲜文、西双版纳新傣文、西双版纳老傣文、维吾尔文、哈萨克文、柯尔克孜文、蒙古文、德宏傣文等。

在线预览|GB 18030-2022
国家标准 - 全国标准信息公共服务平台 
GB18030 编码范围, GB18030 编码表
在线字体查看器 - bejson在线工具 

刨根究底字符编码之六——简体汉字编码中区位码、国标码、内码、外码、字形码的区别及关系

刨根究底字符编码之七——ANSI编码与代码页(Code Page)
 

二、unicode与UTF-8、UTF-16、UTF-32关系

 基本概念
unicode:统一码_百度百科  

字节本地化节省内容存储空间,却给跨区域传递和显示造成极大不便。为了解决这个痛点,有人就想到了,将所有国家或地区的本地化的字符全部进行统一编码。这就是unicode字符集及编码。

unicode 本身既包含字符集,又包含编码。unicode字符集,囊括了各个国家或地区的本地化的字符集,甚至历史上出现过的和虚构的字符集。unicode字符集分成17个平面,全世界常用的字符绝大部分都在第0号平面(BMP)中,这里面囊括了GB2312中的字符集:深入理解“字符编码模型” - 掘金 

unicode 第0个平面(BMP) 字符编码 及 内部分区 - 365建站网 

unicode字符集可以用utf8(Unicode Transformation Format 8bit),ucs2(universol charactor set 2 byte)(utf16),ucs4(utf32)等编码方案来编码。 usc4编码就是unicode编码。
utf8、utf16、utf32是用于编码unicode字符集的编码方案!!!GB2312编码是用于编码GB2312字符集的编码方案!!!
换句话说,unicode字符集有utf8、utf16(大小端)、utf32(大小端)三种编码方案,而GB2312字符集只有GB2312一种编码方案。
汉字字符集编码查询;中文字符集编码:GB2312、BIG5、GBK、GB18030、Unicode

UTF-8编码兼容ASCII编码,采用一个或多个字节表示一个字符。
UTF-16不兼容ASCII编码,UTF-16采用两个或两个的倍数的字节表示一个字符。
UTF-32不兼容ASCII编码,采用四个字节表示一个字符,现在unicode所表示的字符集还没有超出范围,如果超出了所表示的范围,会出现四个或四个的倍数的字节表示一个字符。

unicode 规定的 BOM 

unicode字符集的各种编码方案的具体编码规则参考:
所谓编码--泛谈ASCII、Unicode、UTF-8、UTF-16、UCS-2等编码格式_-CSDN博客 
UTF-8, UTF-16, UTF-32编码的具体算法 - 知乎

三、操作系统、文件的字符编码、程序的字符编码

windows默认采用ansi(有的时候是一个字节,有的时候是两个字节)编码方案,windows下创建文本文件默认都是ansi编码。linux则默认采用UTF-8编码。
windows下识别ansi是GBK,还是无BOM的utf8编码是个比较重要的工作,一般步骤是先当做无BOM的utf8编码进行逐个字符解读,如果遇到非法utf8字符,就认为是ansi编码,又重新回去解读。
windows的记事本(notepad)程序,将内容保存成ansi编码的时候,如果遇到了超出GBK编码的字符,就会将该字符用值为0x3f3f的两个字节进行替换,并导致该字符遗失。淡然保存的时候一般会有下面这样的提示:


因为“𬌗”字在用两字节编码的GBK中没有,但在GB18030中超出GBK的部分中才有,需要用四个字节才能保存,而ansi只允许最多两字节编码。但是utf8编码就没有字节数的限制。​​​​​​

1、ansi 与utf-16不是同一种编码方案!!!ansi编码基本采用本地化编码,用于对本地化字符集编码。utf-16编码unicode字符集,两者字符集是不同的。
2、ansi一般用于windows的文件存储的编码方案,utf-16一般用于计算机编码方案。比如java中,char类型和string类型就是使用utf-16。windows的C++中wchar_t类型也是utf-16(或usc-2,windows操作系统是支持utf-16的,程序具体怎么支持,与代码实现有很大关系的!!!)。

GNU Libc规定C++中的wchar_t为32位,采用的是utf-32(usc-4)编码方案。linux下C/C++ 的wchar_t基本都是32位的,采用utf-32编码。

举个例子:

AA你好  在gb2312字符集中,编码为:0x41 0x41 0xC4E3 0xBAC3
AA你好 在unicode字符集中,采用4字节编码方案,编码为:
0x00000041 0x00000041 0x00004F60 0x0000597D
AA你好 utf16编码为:0x0041 0x0041 0x4F60 0x597D

wchar_t *ptr = L"AA你好"; //vs下的wchar_t采用utf16编码:0x0041 0x0041 0x4F60 0x597D

BOM(字节顺序标记(ByteOrderMark))_百度百科,字节顺序标记,出现在文本文件头部,这个概念由Unicode编码规范推出,在Unicode编码标准中用于标识文件是采用哪种格式的编码。
由于linux的文件保存比unicode的发展都要早,早期的文本都是ascii编码(因为计算机在英语国家诞生,都使用英文,当时ascii编码就足够了),后来随着计算机产业发展和推广,出现本地化字符编码需求和统一的unicode编码,也出现了utf8和ansi这样的兼容此前存在的ascii编码的文件 的编码方案,为了无障碍读取写入此前存在的ascii编码的文件,linux采用兼容ascii编码的utf8编码,所以linux下默认读取和写入文件的字符编码方式是utf8,这就出现 linux写文件时不写入bom 的现状。
windows中也出现同样的情况,windows采用兼容ascii的ansi编码,windows写入和读取文件的字符编码方式是ansi ,因为没有采用unicode编码,没有bom的概念,这就出现 windows写文件时没有bom的现状。
这些都是操作系统自带软件的行为。

四、ansi编码与unicode编码转换

将一个字符从GB2312字符集编码转换成unicode的utf8编码,需要先将找到字符的unicode编码,然后再进行utf8编码。不过unicode没有将GB2312的字符集打散,而是按照GB2312的顺序集中在一起,可以直接通过某个公式将GB2312编码转换成unicode编码,反之亦然参考。

//下面是windows下编码转换的函数
//wideChar 是wchar_t类型,一个字符由两个或两个的倍数的字节组成,采用unicode的utf16编码方案,
//utf8 是char 类型,一个字符由一个或多个字节组成,采用unicode的utf8编码方案
//ansi 是char类型,一个字符由一个或两个字节组成,中大陆一般采用GBK或GB2312的编码方案。

std::string wideCharToUtf8(std::wstring s_unicode)
{
#ifdef WIN32
    std::string dest;
    int len = WideCharToMultiByte(CP_UTF8 //传入数据的编码方案  https://docs.microsoft.com/zh-cn/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar
    , 0, s_unicode.c_str(), -1, NULL, 0, NULL, NULL);
    if (len <= 0)
    {
        return dest;
    }
    char *pbuffer = (char*)malloc(sizeof(char)*len);
    memset(pbuffer, 0, sizeof(char)*len);
    WideCharToMultiByte(CP_UTF8, 0, s_unicode.c_str(), s_unicode.size(), pbuffer, len, NULL, NULL);
    dest = pbuffer;
    free(pbuffer);
    return dest;
#else
    return std::string();
#endif
}


std::wstring utf8ToWideChar(std::string s_utf8)
{
#ifdef WIN32
    std::wstring dest;
    int len = MultiByteToWideChar(CP_UTF8, 0, s_utf8.c_str(), -1, NULL, 0);
    if (len <= 0)
    {
        return dest;
    }
    wchar_t *pbuffer = (wchar_t*)malloc(sizeof(wchar_t)*len);
    memset(pbuffer, 0, sizeof(wchar_t)*len);
    MultiByteToWideChar(CP_UTF8, 0, s_utf8.c_str(), s_utf8.size(), pbuffer, len);
    dest = pbuffer;
    free(pbuffer);
    return dest;
#else
    return std::string();
#endif
}


std::wstring ansiToWideChar(std::string s_ansi)
{
#ifdef WIN32
    std::wstring dest;
    int len = MultiByteToWideChar(CP_ACP, 0, s_ansi.c_str(), -1, NULL, 0);
    if (len <= 0)
    {
        return L"";
    }
    wchar_t *pbuffer = (wchar_t*)malloc(sizeof(wchar_t)*len);
    memset(pbuffer, 0, sizeof(wchar_t)*len);
    MultiByteToWideChar(CP_ACP, 0, s_ansi.c_str(), s_ansi.size(), pbuffer, len);
    dest = pbuffer;
    free(pbuffer);
    return dest;
#else
    return std::string();
#endif

}


std::string wideCharToAnsi(std::wstring s_unicode)
{
#ifdef WIN32
    std::string dest;
    int len = WideCharToMultiByte(CP_ACP, 0, s_unicode.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (len <= 0)
    {
        return "";
    }
    char *pbuffer = (char*)malloc(sizeof(char)*len);
    memset(pbuffer, 0, sizeof(char)*len);
    WideCharToMultiByte(CP_ACP, 0, s_unicode.c_str(), s_unicode.size(), pbuffer, len, nullptr, nullptr);
    dest = pbuffer;
    free(pbuffer);
    return dest;
#else
    return std::string();
#endif
}


std::string ansiToUtf8(std::string s_ansi)
{
    return  wideCharToUtf8(ansiToWideChar(s_ansi));
}
std::string utf8ToAnsi(std::string s_utf8)
{
    return wideCharToAnsi(utf8ToWideChar(s_utf8));
}

五、文字显示

对于字体库不支持的文字 ,操作系统会显示空白。比如 “𬌗” 字,在gbk中不存在,在GB18030中存在,如果当前使用的个性字体库 只有支持GBK 或者 支持GB2312 甚至 不支持汉字,那么这个字就不会被渲染,而显示空白。而在文件中,以ansi编码保存 “𬌗” 这样GBK中不存在而在GB18030中存在的字符时,会导致字符丢失,windows的记事本在这个时候会给出提示:

因为ansi编码与uft8编码的文件都没有bom,在windows中程序识别文件是ansi编码还是uft8编码就显得有意思了,通常会先按utf8读取文件,但遇到utf8来说不合法的字节时,就说明文件不是utf8编码转而使用ansi编码进行读取;如果utf8读取,没有遇到不合法的字节,就认为是utf8编码。
刨根究底字符编码之十六——Windows记事本的诡异怪事:微软为什么跟联通有仇? - 知乎

第十六课,在Windows系统中显示文字-CSDN博客 
汉字点阵原理&字模读取与显示_中文点阵字模-CSDN博客 
windows 查看ttf字体_如何查看ttf中字符-CSDN博客 
初探ttf字体文件的内部格式 - 知乎 
freetype矢量字体 
嵌入式应用-详解移植并使用freetype显示文字_lcd_put_pixel是哪个头文件里的-CSDN博客  
C/C++ 利用FreeType提取字体文件的字形_ft_new_face-CSDN博客 
C++ FTFont::CharMap方法代码示例 
fontTools库来检测字体文件中是否包含某字符_qt 检测字体是否能正常显示-CSDN博客 
cmap 表 - huobengluantiao8 - ITeye博客

如何利用windows自带的矢量字库提取字模_如何提取windows字库合成词-CSDN博客

汉字字形码 - COOC

初探ttf字体文件的内部格式 - 知乎

关于文字的渲染 可以使用std_truetype.h 中的stbtt_BakeFontBitmap 来进行,
参考:stb_truetype解析ttf字体获取顶点信息_virwin的博客-CSDN博客  D3D11的简单字体_x-2010的博客-CSDN博客_d3d11 绘制文字 
绘制文本 (GDI+) - Win32 apps | Microsoft Learn 
D3D绘制字体_d3d 文字-CSDN博客 
DirectX学习笔记(十):3D字体的实现及用ID3DXFont接口绘制文本_directx font-CSDN博客 
http://www.fmddlmyy.cn/text24      //gb18030-2000/gb18030-2005/unicode/二进制  介绍
查看字符编码(UTF-8) //utf8 在线转换 

字符集与字符编码_謝平原的博客-CSDN博客_字符集 编码  
非常详细的字符编码讲解,ASCII、GB2312、GBK、Unicode、UTF-8等_哔哩哔哩_bilibili  

率先支持GB 18030-2022,腾讯搜狗输入法通过最高级别认证 
阿里巴巴普惠体 3.0 正式发布:支持新国标 GB18030-2022,简体中文・7 字重 - IT之家 
方正推出五款GB18030字库:书宋、仿宋、黑体、楷体、宋一-字体资讯-字客网 
GB18030 编码范围, GB18030 编码表 
Windows 11 更新新版简体中文字体(附下载) - 知乎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值