GB2132转UTF-8

2023-05-16

背景

单片机端常用的中文显示字符集是GB2312, 相对于UTF-8表示中文时更节省空间, 但是Linux端为了通用及兼容性常采用UTF-8作为字符编码, 为了保持编码的的统一, 网络通信时单片机内部将GB2312转为UTF-8发送给Linux, 于是就有了这个动机;

常见字符编码介绍

GB2312

GB/T 2312GB/T 2312–80GB/T 2312–1980 是中华人民共和国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,通常简称GB(“国标”汉语拼音首字母),又称GB0

GB/T 2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。其收录的汉字已经覆盖中国大陆99.75%的使用频率。但对于人名、古汉语等方面出现的罕用字和繁体字,GB/T 2312不能处理,因此后来GBK及GB 18030汉字字符集相继出现以解决这些问题。

有两种不同的GB/T 2312实现,在它们之间存在少量的差别,其中至少有一个是错误的。GBK子集与GBK/GB 18030兼容,GB2312.TXT则不兼容。

分区表示

GB/T 2312 中对所收汉字进行了“分区”处理,每区含有94个汉字/符号,共计94个区。实际上,GB/T 2312 只使用了87区

用所在的区和位来表示字符(实际上就是码位)的方法称为区位码(或许叫“区位号”更为恰当)。例如“万”字在45区82位,所以“万”字的区位码是 45-82(45是“区码”,82是“位码”)。在储存进电脑时,电脑会在区位码上加上特定数字后才保存进内存以确保和其他编码兼容(如 ASCII)。转码后,区位码的“区码”会变成“高位字节”,而“位码”会变成“低位字节”。

下列是 GB/T 2312 分区后在区段内储存的字符:

  • 01~09区(682个):特殊符号、数字、英文字符、制表符等,包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母等在内的682个全角字符;
  • 10~15区:空区,留待扩展;在附录3,第10区推荐作为 GB 1988–80 中的94个图形字符区域(即第3区字符之半形版本)。
  • 16~55区(3755个):常用汉字(也称一级汉字),按拼音排序;
  • 56~87区(3008个):非常用汉字(也称二级汉字),按部首/笔画排序;
  • 88~94区:空区,留待扩展。

字节结构

在 GB 2312 内,每个汉字及符号的码位使用两个字节来表示。第一个字节称为“高位字节”,对应分区的编号(把区位码的“区码”加上特定值);第二个字节称为“低位字节”,对应区段内的个别码位(把区位码的“位码”加上特定值)。

把“区码”和“位码”分别加上160(十六进制为0×A0)也可以得到相同的机内码表示,这种格式也就是EUC。使用GB/T 2312的程序通常采用 EUC 储存方法,以便兼容于 ASCII。这种格式称为EUC-CN(此外还有ISO 2022-CN)。浏览器编码表上的“GB2312”就是指这种表示法。

GBK

汉字内码扩展规范,简称GBK,全名为《汉字内码扩展规范(GBK)》1.0版。GBK收录21886个汉字和图形符号,其中汉字(包括部首和构件)21003个,图形符号883个。GBK的K为“扩展”的汉语拼音(kuòzhǎn)第一个声母。

GBK 只为 “技术规范指导性文件”,不属于国家标准。国家质量技术监督局于2000年3月17日推出了GB 18030-2000标准,以取代GBKGB 18030-2000除保留全部GBK编码汉字,在第二字节把能使用范围再度进行扩展,增加了大约一百个汉字及四字节编码空间,但是 将GBK作为子集全部保留

根据微软资料,GBK是对GB2312-80的扩展,也就是CP936字码表(Code Page 936)的扩展(之前CP936GB 2312-80一模一样),最早实现于Windows 95简体中文版。

编码方式

字符有一字节和双字节编码,00–7F范围内是第一个字节,和ASCII保持一致,此范围内严格上说有96个文字和32个控制符号。

之后的双字节中,前一字节是双字节的第一位。总体上说第一字节的范围是81–FE(也就是不含80和FF),第二字节的一部分领域在40–7E,其他领域在80–FE

具体来说,定义的是下列字节:

GBK的编码范围

范围第1字节第2字节编码数字数
水准GBK/1A1–A9A1–FE846717
水准GBK/2B0–F7A1–FE6,7686,763
水准GBK/381–A040–FE (7F除外)6,0806,080
水准GBK/4AA–FE40–A0 (7F除外)8,1608,160
水准GBK/1A8–A940–A0 (7F除外)192166
用户定义AA–AFA1–FE564
用户定义F8–FEA1–FE192
用户定义A1–A740–A0 (7F除外)672
合计23,94021,886

双字节符号可以表达的64K空间如下图所示。绿色和黄色区域是GBK的编码,红色是用户定义区域。没有颜色区域是不正确的代码组合。

GBK

GB18030

GB 18030,全称《信息技术 中文编码字符集》,是中华人民共和国国家标准所规定的变长多字节字符集。其对GB 2312-1980完全向后兼容,与GBK基本向后兼容,并支持Unicode(GB 13000)的所有码位。GB 18030共收录汉字70,244个。

GB 18030主要有以下特点:

  • 采用变长多字节编码,每个字可以由1个、2个或4个字节组成。
  • 编码空间庞大,最多可定义161万个字符。
  • 完全支持Unicode,无需动用造字区即可支持中国国内少数民族文字、中日韩和繁体汉字以及emoji等字符。
  • GB 18030在微软视窗系统中的代码页为54936。

字节结构

GB 18030包含三种长度的编码:单字节的ASCII双字节的GBK(略带扩展)、以及用于填补所有Unicode码位的四字节UTF区块。GBK双字节部分通过查表定义,而四字节部分则根据之前两个部分没有提到的通用字符集码位顺序填补。

GB18030编码

一、二字节区块基本就是GBK编码,另外加上了专门的欧元字符、竖排版本的标点符号,以及造字区对Unicode造字区的对应。四字节区块可以视作两段形似GBK二字节区块结构的部分,每段的第一字节可以为0x81到0xFE,第二字节为0x30到0x39。由于结构类似,能够安全于GBK的字符串搜索程序对于GB 18030来说也基本安全(正如基于字节的搜索程序对于EUC、UTF-8也基本安全一般。)
GB18030四字节半码编码区

Unicode

Unicode,联盟官方中文名称为统一码,是计算机科学领域的业界标准。它整理、编码了世界上大部分的文字系统。

在表示一个Unicode的字符时,通常会用“U+”然后紧接着一组十六进制的数字来表示这一个字符。在基本多文种平面里的所有字符,要用四个数字(即2字节,共16位,例如U+4AE0,共支持六万多个字符);在零号平面以外的字符则需要使用五或六个数字。旧版的Unicode标准使用相近的标记方法,但却有些微小差异:在Unicode 3.0里使用“U-”然后紧接着八个数字,而“U+”则必须随后紧接着四个数字。

编码方式

统一码的编码方式与ISO 10646的通用字符集概念相对应。目前实际应用的统一码版本对应于UCS-2,使用16位的编码空间。也就是每个字符占用2个字节。这样理论上一共最多可以表示216(即65536)个字符。基本满足各种语言的使用。实际上当前版本的统一码并未完全使用这16位编码,而是保留了大量空间以作为特殊使用或将来扩展。

上述16位统一码字符构成 基本多文种平面。最新的统一码版本定义了16个辅助平面,两者合起来至少需要占据21位的编码空间,比3字节略少。但事实上辅助平面字符仍然占用4字节编码空间,与UCS-4保持一致。未来版本会扩充到ISO 10646-1实现级别3,即涵盖UCS-4的所有字符。UCS-4是更大而尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字节。理论上最多能表示231个字符,完全可以涵盖一切语言所用的符号。

基本多文种平面的字符的编码为U+hhhh,其中每个h代表一个十六进制数字,与UCS-2编码完全相同。而其对应的4字节UCS-4编码后两个字节一致,前两个字节则所有位均为0。

Unicode 将编码空间

第 0 平面(或者说基本多文种平面)中的码点,都可以用一个 UTF-16 单位来编码,或者以 UTF-8 来编码的话,会使用一、二或三个字节。而第 1 到 16 平面(或称辅助平面)中的码点,UTF-16 会以代理对的方式来使用,而 UTF-8 则会编码成 4 个字节。

在每个平面中,会先将相关的字符集结为区段的形式。虽然区段可以是任意大小,但会以 16 个码点的倍数,且通常是 128 个码点的倍数。而一份文稿中使用到的区段,可能会散布在多个区段中。

实现方式

Unicode的实现方式不同于编码方式。一个字符的Unicode编码确定。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式Unicode Transformation Format,简称为UTF)。

例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用UTF-8编码,这是变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1识别。这样对以7位ASCII字符为主的西文文档就大幅节省了编码长度(具体方案参见UTF-8)。类似的,对未来会出现的需要4个字节的辅助平面字符和其他UCS-4扩充字符,2字节编码的UTF-16也需要通过一定的算法转换。

UTF-8

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码。属于Unicode标准的一部分。

UTF-8使用一至六个字节为每个字符编码:

  1. 128个US-ASCII字符只需一个字节编码(Unicode范围由U+0000至U+007F)。
  2. 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要两个字节编码(Unicode范围由U+0080至U+07FF)。
  3. 其他基本多文种平面(BMP)中的字符(这包含了大部分常用字,如大部分的汉字)使用三个字节编码(Unicode范围由U+0800至U+FFFF)。
  4. 其他极少使用的Unicode 辅助平面的字符使用四至六字节编码(Unicode范围由U+10000至U+1FFFFF使用四字节,Unicode范围由U+200000至U+3FFFFFF使用五字节,Unicode范围由U+4000000至U+7FFFFFFF使用六字节)。

对上述提及的第四种字符而言,UTF-8使用四至六个字节来编码似乎太耗费资源了。但UTF-8对所有常用的字符都可以用三个字节表示,而且它的另一种选择,UTF-16编码,对前述的第四种字符同样需要四个字节来编码,所以要决定UTF-8或UTF-16哪种编码比较有效率,还要视所使用的字符的分布范围而定。

2003年11月UTF-8RFC 3629重新规范,只能使用原来Unicode定义的区域,U+0000到U+10FFFF,也就是说 最多四个字节
UTF-8 4byte

编码方式

utf-8 编码

实现思路及代码

参考暴力法获得gb2312和utf-8互转的查找表文章中, 利用Python中的编码库, 进行转换解析打印出可用的对应表;

# -*- coding: UTF-8 -*-

import struct


global table_file, count

count = 0
table_file = open("gbk_to_utf8.c", "w+")
table_file.write("static const unsigned short gbkUcs2Tab[][2]={\n")

for i in range(65536):
    hi_byte = i >> 8
    lo_byte = i & 0xff
    hz = struct.pack('<BB', hi_byte, lo_byte)
    try:
        hz = hz.decode(encoding='gb2312')   # 按GB2312解码
        if len(hz) == 1:
            code_gb2312 = hz.encode(encoding='gb2312')
            gb_val = code_gb2312[0] * 256 + code_gb2312[1]
            str = '\t{' + hex(gb_val) + ', ' + hex(ord(hz)) + '}, //' + hz + '\n'
            print(str, end='')
            table_file.write(str)
            count+=1
    except UnicodeError as unierr:
        pass

print('valid num %d' % count)
table_file.write("};\n")
table_file.close()

然后在生成的gb2312_to_utf8.c中添加以下函数:

unsigned short gbk_to_ucs2(unsigned short gbk)
{

    int low = 0, high = sizeof(gbkUcs2Tab)/4 - 1;
    while(low <= high){
        int mid = (high - low) / 2 + low;
        int num = gbkUcs2Tab[mid][0];
        if (num == gbk) {
            return gbkUcs2Tab[mid][1];
        } else if (num > gbk) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return 0;
};

int enc_ucs2_to_utf8_one(unsigned short unic, unsigned char *pOutput)
{
    if ( unic <= 0x007F )
    {
        // * U-00000000 - U-0000007F:  0xxxxxxx
        *pOutput     = (unic & 0x7F);
        return 1;
    }
    else if ( unic >= 0x0080 && unic <= 0x07FF )
    {
        // * U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
        *(pOutput+1) = (unic & 0x3F) | 0x80;
        *pOutput     = ((unic >> 6) & 0x1F) | 0xC0;
        return 2;
    }
    else if ( unic >= 0x0800 && unic <= 0xFFFF )
    {
        // * U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
        *(pOutput+2) = (unic & 0x3F) | 0x80;
        *(pOutput+1) = ((unic >>  6) & 0x3F) | 0x80;
        *pOutput     = ((unic >> 12) & 0x0F) | 0xE0;
        return 3;
    }

    return 0;
}

int gbk_convert_to_utf8(unsigned char* gbk, unsigned char* utf8, int utf8_size)
{
    int i, gbk_len, gbk_val, ucs2_val, utf8_len, ret;
    unsigned char tmp[4];

    gbk_len = strlen((char *)gbk);
    utf8_len = 0;

    for(i = 0; i < gbk_len; i++)
    {
        ret = 0;
        if(gbk[i] <= 0xA0) {
            tmp[0] = gbk[i];
            ret = 1;
        }
        else if ((i+1 < gbk_len) && gbk[i+1] > 0xA0)
        {
            gbk_val = (gbk[i] << 8) + gbk[i+1];
            i++;
            ucs2_val = gbk_to_ucs2(gbk_val);
            ret = enc_ucs2_to_utf8_one(ucs2_val, tmp);
        }
        else
        {
            //only one continuous byte biger than 0xA0, Discard the character
            continue;
        }
        if(utf8_len + ret > utf8_size) {
            return -2;
        }
        memcpy(utf8+utf8_len, tmp, ret);
        utf8_len += ret;
    }
    return utf8_len;
}

然后使用gbk_convert_to_utf8进行转换即可;

参考文章

  1. GB2312
  2. 汉字内码扩展规范
  3. Code page 936 (Microsoft Windows)
  4. GB18030
  5. Unicode
  6. Unicode字符平面映射
  7. UTF-8
  8. GB2312、GBK、GB18030 这几种字符集的主要区别是什么
  9. 暴力法获得gb2312和utf-8互转的查找表
    10.stm32单片机平台上ASCII(GBK,GB2312)转unicode转UTF-8
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GB2132转UTF-8 的相关文章

随机推荐

  • GPS模块(GPS-NEO-6M)

    ATK NEO 6M GPS 模块简介 ATK NEO 6M V23 模块 xff0c 是 ALIENTEK 生产的一款高性能 GPS 模块 xff0c 模块核心采用 UBLOX公司的 NEO 6M 模组 xff0c 具有 50 个 通道
  • toCharArray()

    toCharArray 是将一个字符串内容转换为字符数组 xff0c 例如 String str 61 34 abc 34 System out println str toCharArray 43 34 34 将输出a b c 转载于 h
  • 基于Arduino的GPS数据解析程序

    这篇博客讲了我利用arduino来解析和转发原始nema 0813数据的思想和实现方法 因为arduino比较简单 xff0c 无法实现串口数据接收中断 xff0c 所以都写在主循环里面了 不知道代码存在何种缺陷和漏洞 xff0c 欢迎大家
  • VsCode安装和配置c/c++环境(超完整,小白专用)

    文章目录 1 vsCode配置C C 43 43 环境 1 vsCode下载和安装 1 下载Microsoft vsCode2 安装vsCode3 下载中文插件2 MinGW编译器下载和配置 1 下载MinGW2 下载后放到自己方便的目录
  • 各版本esp32和esp8266开发板引脚图(附各开发板特殊通信接口如IIC、SPI接口等默认引脚查看方法)

    目录 esp32 GPIO可用资源 1 esp32开发板 2 esp32开发板 查看特殊通信接口的方法 esp8266 esp32 GPIO可用资源 GPIO 6 11 连接到SPI Flash GPIO 34 39 只能作为输入且没有内部
  • GPS数据解析、可视化及经纬度距离计算

    一 GPS数据解析 根据NMEA协议 xff0c 我们从传感器上接收到的GPS经纬度数据格式如下 xff1a 例 xff1a GPRMC 024813 640 A 3158 4608 N 11848 3737 E 10 05 324 27
  • C语言知识点小结 | 指针 数组 结构体 堆栈 内存分配

    不掌握指针就是没有掌握C的精华 地址指向该变量单元 xff0c 地址即指针 在C C 43 43 语言中定义一个指针 xff0c 就是在栈区开辟一个内存空间用来存放它指向的内存地址 xff0c 然后给指针赋值 xff0c 就是把地址值赋值给
  • 西门子PLC S7-200SMART Modbus TCP通讯的步骤和要点

    Modbus TCP是一个非常传统 xff0c 应用广泛的通讯协议 xff0c 很多智能设备都支持该协议 西门子S7 200SMART及1200 1500系列都免费支持 xff08 300和400还是要高昂收费 xff09 xff0c 并且
  • GPRM/GNRMC定位信息的读取与解析

    GPRM GNRMC定位信息的读取与解析 参考网址 xff1a http www cnblogs com 88223100 p GPRM GNRMC Transform html 帧头 UTC时间 状态 纬度 北纬 南纬 经度 东经 西经
  • 基于Arduino 开发 MAX30102 LM35 SSD1306 观察血氧、心率和温度血氧仪

    本项目第一版本实现在arduino框架下通过MAX30102 对血氧和心率 进行实时监控 xff0c 通过LM35 对温度进行监控 所有数值在 ssd 1306 上进行显示 在血氧低过一定数值的时 xff0c 设备会通过蜂鸣器发出警报 第二
  • Python中max函数key的用法详解

    一 背景 起源于一个问题 xff1a 怎样找到字符串中出现次数最多的字符 其实使用max函数就能很轻松的解决这个问题 xff1a 代码 xff1a str1 61 34 AAAaaa8888899sssss 34
  • 查询选修了全部课程的学生姓名

    SELECT SN FROM S WHERE NOT EXISTS SELECT FROM C WHERE NOT EXISTS SELECT FROM SC WHERE SNO 61 S SNO AND CNO 61 C CNO 今天在看
  • Android11小黄鸟安装CA证书以及解决抓包没网问题

    目录 安装CA证书解决没网解决没有system读写权限 安装CA证书 首先没有CA证书是这个样子的 1 准备一个MT管理器 2 进入到 data data com guoshi httpcanary premium cache 目录找到Ht
  • libcurl异步方式使用总结

    原文链接 xff1a https www cnblogs com Newdawn p 10051231 html libcurl这个库的同步方式很简单 xff0c 不做介绍 xff0c 而异步方式很难理解 xff0c 本博客参考官网的dem
  • 开源项目中的法律风险

    引言 写这篇博客的契机是我厂刚好开了一次这样的培训 xff0c 听了以后觉得很有收获 碰巧自己最近也在写开源项目 xff0c 因此觉得还是有必要写一下 有小伙伴提到 xff0c 这种问题 xff0c 去网上找那个指导你如何选择 LICENS
  • UTF8中文编码范围

    简介 UTF 8有点类似于Haffman编码 xff0c 它将Unicode编码为 xff1a 00000000 0000007F的字符 xff0c 用单个字节来表示 xff1b 00000080 000007FF的字符用两个字节表示 xf
  • MLO/uboot-spl.bin和uboot.img/uboot.bin

    前段时间使用TI的am4378芯片 xff0c 发现系统在SD卡启动的时候 xff0c 启动文件使用的是MLO和uboot img xff1b 而Norflash和eMMC启动的时候使用的是 uboot spl bin和uboot bin
  • 身份证校验码规则

    背景 项目中有部分功能需要验证用户身份 为了防止用户随便输入身份信息 因此要对输入数据进行验证 于是参照百科提供的规则进行了实现 公民身份号码是特征组合码 xff0c 由十七位数字本体码和一位数字校验码组成 排列顺序从左至右依次为 xff1
  • 【C语言】代码分析--条件编译及编译预处理阶段

    来自博客园 Rusty 39 s code 一 C语言由源代码生成的各阶段如下 xff1a C源程序 xff0d gt 编译预处理 xff0d gt 编译 xff0d gt 优化程序 xff0d gt 汇编程序 xff0d gt 链接程序
  • GB2132转UTF-8

    背景 单片机端常用的中文显示字符集是GB2312 相对于UTF 8表示中文时更节省空间 但是Linux端为了通用及兼容性常采用UTF 8作为字符编码 为了保持编码的的统一 网络通信时单片机内部将GB2312转为UTF 8发送给Linux 于