字符编码——第一平面的unicode分析

本篇小小探索了下 unicode 第一片面的字符。

一、什么是unicode?

Unicode,全称为Unicode标准(The Unicode Standard),其官方机构Unicode联盟所用的中文名称为统一码,又译作万国码、统一字符码、统一字符编码,是信息技术领域的业界标准,其整理、编码了世界上大部分的文字系统,使得电脑能以通用划一的字符集来处理和显示文字,不但减轻在不同编码系统间切换和转换的困扰,更提供了一种跨平台乱码问题解决方案。Unicode由非营利机构Unicode联盟(Unicode Consortium)负责维护,该机构致力让Unicode标准取代既有的字符编码方案,因为既有方案编码空间有限,亦不适用于多语环境。

------取自维基百科

拆解一下关键信息:

  • Unicode:是一种标准,包括字符集定义、字符编码和解码方式。字符集支持且不断扩充,有多种编码解码方式。
  • 乱码解决方案:通过全局定义全球各个国家的通用字符,统一编码和解码,避免跨平台跨国家互联网传输的乱码问题。乱码的本质是字符集不一致或编解码方式的不一致,导致信息错乱。
二、为什么了解第一平面?

目前的统一码字符分为17组编排,每组称为平面(Plane),以 0 到 16 编号。每平面有65536(216)点代码,但目前只用了少数平面。

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

------取自维基百科

第一个平面称为基本多语言平面(Basic Multilingual Plane,简称BMP),这个平面涵盖了当今世界上最常用的字符。

如果业务持久化的编码是utf-8而不是utf8mb4的话,相当于字符的传输过程中字符集更小了,需要限制字符字节数,不然数据乱码问题会很严重。

三、第一平面的现状?

这里取最常用的第一平面,我们了解一下这一平面的现状。utf8是比较通用的变字节编码方式,这里我们分析下utf8编码下的字符编码。分析阶段只贴核心代码,全部代码末尾贴出,大家可自行尝试。

Go 复制代码
//核心实现:
for i := 0; i < 65536; i++ {
	size := fmt.Sprintf("%d", utf8.RuneLen(rune(i)))
	line := fmt.Sprintf("%d    %c    %s    %x\n", i, i, size, i)
	if size == "1" {
		_, _ = file.Write([]byte(line))//size_1.txt
	} else if size == "2" {
	    _, _ = file2.Write([]byte(line))//size_2.txt
    } else if size == "3" {
		_, _ = file3.Write([]byte(line))//size_3.txt
    } else {
		_, _ = file4.Write([]byte(line))//size_4.txt
	}
}
1 运行结果

wc -l size_1.txt
128 size_1.txt

wc -l size_2.txt
1920 size_2.txt

wc -l size_3.txt
61440 size_3.txt

wc -l size_4.txt
2048 size_4.txt

2 结论

第一平面65536个码点,其中有效字符128+1920+61440=63488个。

|-------------|-------|-----------|
| 字节数 | 码点数 | 备注 |
| 1字节 | 128 | |
| 2字节 | 1920 | |
| 3字节 | 61440 | |
| 非合法utf-8序列值 | 2048 | 变字节编码规则限制 |

3 为什么会存在非合法utf-8序列值?

UTF-8需要兼容ASCII,所以也需要有前缀码来控制,前缀规则如下:

  • 如果首字节以 0 开头,则是单字节编码(即单个单字节码元);
  • 如果首字节以 110 开头,则是双字节编码(即由两个单字节码元所组成的双码元序列);
  • 如果首字节以 1110 开头,则是三字节编码(即由三个单字节码元所组成的三码元序列),以此类推。
4 详细结果列表概览
四、详细代码
Go 复制代码
package main
import (
	"fmt"
	"unicode/utf8"
	"os"
	"log"
)

func main() {
	file1name := "size_1.txt"
	file2name := "size_2.txt"
	file3name := "size_3.txt"
	file4name := "size_4.txt"
	file, err := os.OpenFile(file1name, os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {// 如果文件不存在,创建文件
		if os.IsNotExist(err) {
			file, err = os.Create(file1name)
			if err != nil {
				log.Fatal(err)
			}
			defer file.Close()
		} else {
			log.Fatal(err)
		}
	}
	defer file.Close()
	file2, err := os.OpenFile(file2name, os.O_APPEND|os.O_WRONLY, 0644)
        if err != nil {// 如果文件不存在,创建文件
                if os.IsNotExist(err) {
                        file2, err = os.Create(file2name)
                        if err != nil {
                                log.Fatal(err)
                        }
                        defer file2.Close()
                } else {
                        log.Fatal(err)
                }
        }
        defer file2.Close()
	file3, err := os.OpenFile(file3name, os.O_APPEND|os.O_WRONLY, 0644)
        if err != nil {// 如果文件不存在,创建文件
                if os.IsNotExist(err) {
                        file3, err = os.Create(file3name)
                        if err != nil {
                                log.Fatal(err)
                        }
                        defer file3.Close()
                } else {
                        log.Fatal(err)
                }
        }
        defer file3.Close()
	file4, err := os.OpenFile(file4name, os.O_APPEND|os.O_WRONLY, 0644)
        if err != nil {// 如果文件不存在,创建文件
                if os.IsNotExist(err) {
                        file4, err = os.Create(file4name)
                        if err != nil {
                                log.Fatal(err)
                        }
                        defer file4.Close()
                } else {
                        log.Fatal(err)
                }
        }
        defer file4.Close()


	for i := 0; i < 65536; i++ {
		size := fmt.Sprintf("%d", utf8.RuneLen(rune(i)))
		line := fmt.Sprintf("%d  %c      %s	%x\n", i, i, size, i)
		if size == "1" {
			_, _ = file.Write([]byte(line))
		} else if size == "2" {
			_, _ = file2.Write([]byte(line))
        } else if size == "3" {
			_, _ = file3.Write([]byte(line))
        } else {
			_, _ = file4.Write([]byte(line))
		}
	}
}
相关推荐
charlie1145141912 天前
通用GUI编程技术——图形渲染实战(四十)——深度缓冲与3D变换:从平面到立体
开发语言·c++·平面·3d·图形渲染·win32
Elastic 中国社区官方博客4 天前
通过受管控的控制平面加速商品陈列优化
大数据·数据库·人工智能·elasticsearch·搜索引擎·平面·ai
FakeOccupational8 天前
【电路笔记 电源模块】“桥接”布局法+电源隔离+GND隔离+统一地平面防干扰
笔记·平面
Evand J8 天前
【MATLAB程序】基于RSSI的RFID二维轨迹定位仿真介绍,EKF滤波增加轨迹定位精度。附下载链接
开发语言·matlab·平面·滤波·定位·导航
Evand J11 天前
【代码介绍】二维平面上的雷达跟踪与UKF(无迹卡尔曼滤波),高精度估计目标轨迹,输出真值、估计值、误差特性等
matlab·平面·雷达·滤波·定位·导航·跟踪
bcbobo21cn11 天前
Three.js绘制三角形网格平面
前端·javascript·平面·三角形面·基础材质
fengfuyao98511 天前
基于遗传算法的分布式电源选址定容优化(考虑环境因素)
算法·matlab·平面
fengfuyao98512 天前
MATLAB计算任意倾斜平面的太阳辐射量,包括直射、散射和反射分量
算法·matlab·平面
RReality13 天前
【Unity Shader URP】平面反射(Planar Reflection)实战教程
ui·平面·unity·游戏引擎·图形渲染·材质
普密斯科技16 天前
齿轮平面度与正反面智能检测方案:3D视觉技术破解精密制造品控难题
人工智能·计算机视觉·平面·3d·自动化·视觉检测