程序员必备:10分钟搞懂各种编码丨另附实战案例

本文介绍了字符编码的基本概念,包括编码字符集、字符编码表(UTF-8 和 UTF-16)以及在 Golang 中的应用。此外,还讲解了二进制编码(Hex 和 Base64)和字节序(大端与小端)的原理,以及在实际操作中的注意事项。通过实例展示了加解密过程中编码和字节序的重要性,强调了正确理解和处理编码问题的必要性。
摘要由CSDN通过智能技术生成

背景

HTTP 协议基于文本传输,字符编码将文本变为二进制,二进制编码将二进制变为文本。TCP 协议基于二进制传输,数据读取时需要处理字节序。本文将介绍常见的字符编码、二进制编码及字节序,并一探 Golang 中的实现。

字符编码

引言:如何把“Hello world”变成字节?

  • Step1:得到要表示的全量字符(字符表)

  • Step2:为每个字符指定一个整数编号(编码字符集)

  • Step3:将编号映射成有限长度比特值(字符编码表)

字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。全世界共使用 5651 种语言,其中使用人数超过 5000 万的语言有 13 种,每种语言有自己的字符。汉语中,一个汉字就是一个字符。英语中,一个字母就是一个字符。甚至看不见的也可以是字符(如控制字符)。字符的集合即为字符表,如英文字母表,阿拉伯数字表。ASCII 码表中一共有 128 个字符。

编码字符集(CCS:Coded Character Set)

为字符表中的每个字符指定一个编号(码点,Code Point),即得到编码字符集。常见有 ASCII 字符集、Unicode 字符集、GB2312 字符集、BIG5 字符集、 GB18030 字符集等。ASCII 字符集中一共有 128 个字符,包括了 94 个可打印字符(英文大小写字母 52 个、阿拉伯数字 10 个、西文符号 32 个)和 34 个控制符或通信专用字符,码点值范围为[0, 128),如下图所示。Unicode 字符集是一个很大的集合,现有容量将近 2^21 个字符,码点值范围为[0, 2^20+2^16)

ASCII字符编码表

字符编码表(CEF:Character Encoding Form)

编码字符集只定义了字符与码点的映射,并没有规定码点的字节表示方式。由于 1 个字节可以表示 256 个编号,足以容纳 ASCII 字符集,因此ASCII 编码的规则很简单:直接将码点值用 uint8 表示即可。对于 Unicode 字符集,容纳 2^21 至少需要 3 字节。可以采用类似 ASCII 的编码规则:直接将编码点值用 uint32 表示即可,这正是 UTF-32 编码

这种一刀切的定长编码方式虽然简单粗暴,弊端也很明显:对于纯英文文本,UTF-32 编码空间占用将是 ACSII 编码的 4 倍,造成极大的空间浪费,几乎没什么人用。有没有更优雅的解决方案?当然,这就是 UTF-8 和 UTF-16,两种当前比较流行的 Unicode 编码方式。

UTF-8

历史告诉我们,成功的设计往往具有包容性。UTF-8 是一个典型,漂亮的实现了对 ASCII 码的向后兼容,以保证可以被大众接受。UTF-8 是目前互联网上使用最广泛的一种 Unicode 编码方式,它的最大特点就是可变长,随码点变换长度(从 1 字节到 4 字节)。text
在这里插入图片描述

大道至简,优雅的设计一定是简单的,UTF-8 的编码规则也诠释了这一点。编码规则如下:

  1. <=127(U+7F)的码点采用单字节编码,与 ASCII 保持一致;

  2. >127(U+7F)的码点采用 N 字节(N 属于 2,3,4)编码,首字节的前 N 位为 1,第 N+1 位为 0,剩余 N-1 个字节的前两位都为 10,剩下的二进制位使用字符的码点来填充。

其中(U+7F)表示 Unicode 的十六进制码点值,即 127。如果觉得编码规则抽象,结合下表更加清晰:

Unicode 码点范围 码点数量 UTF-8 编码格式
0000 0000 ~ 0000 007F 2^7 0xxxxxxx
0000 0080 ~ 0000 07FF 2^11 - 2^7 110xxxxx 10xxxxxx
0000 0800 ~ 0000 FFFF 2^16 - 2^11 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 ~ 0010 FFFF 2^20 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

举个例子,如“汉”的 Unicode 码点是 U+6C49(110 1100 0100 1001),根据上表可得需要 3 字节编码,填充码点值后得到 0xE6 0xB7 0x89(11100110 10110001 10001001)。

根据编码规则,解码也很简单,关键是如何判断连续的字节数:首字节连续 1 的个数即为字节数

需要一提的是,在 MySQL 中,utf8 是“虚假的 utf8”,最大只支持 3 个字节,如果建表时选择 CHARSET=utf8,会导致很多特殊字符和 emoji 表情都无法插入。utf8mb4 才是“真正的 utf8”,mb4 即most bytes 4。为什么 MySQL 中 utf8 最大只支持 3 字节?历史原因,在 MySQL 刚开发那会儿,Unicode 空间只有 2^16,Unicode 委员会还在做 “65535 个字符足够全世界用了”的美梦呢。

UTF-16

在 C/C++ 中遇到的wchar_t类型或 Java 中的char类型,这些类型占内存两个字节,因为 Unicode 中常用的字符都处于[U+0, U+FFFF](基本平面)的范围之内,因此两个字节几乎可以覆盖大部分的常用字符,这正是 UTF-16 编码的一个前提。

相比 UTF-32 与 UTF-8,UTF-16 编码是一个折中:小于(U+FFFF)2^16 的码点(基本平面)使用 2 字节编码,大于(U+FFFF)2^16 的码点(辅助码点)使用 4 字节编码。由于基础平面空间会占用 2 字节的所有比特位,无法像 UTF-8 那样留有“10”前缀。那么问题来了:当我们遇到两个节时,如何判断是 2 字节编码还是 4 字节编码?

UTF-16 的编码的另一个前提:在基本平面内,[U+D800, U+DFFF]是一个空段(空间大小为 2^11),这些码点不对应任何字符。因此,这个空段可以用来映射辅助平面的字符。

辅助平面容量为 2^20,至少需要 20 个二进制位,UTF-16 将这 20 个二进制位分成两半,前 10 位映射在 U+D800 到 U+DBFF(空间大小 2^10),称为高位(H),后 10 位映射在 U+DC00 到 U+DFFF(空间大小 2^10),称为低位(L)。

映射方式采用线性映射。Unicode3.0 中给出了辅助平面字符的转换公式:

H = Math.floor((c-0x10000) / 0x400) + 0xD800

L = (c - 0x10000) % 0x400 + 0xDC00

也就是说,一个辅助平面的码点,被拆成两个基本平面的空段码点表示。如果双字节的值在[U+D800, U+DBFF]中,则要和后续相邻的双字节一同解码。具体编码规则为:

  1. <= (U+FFFF)的码点采用双字节编码,直接将码点使用 uint16 表示;

  2. > (U+FFFF)的码点采用 4 字节编码,作差计算码点溢出值,将溢出值用 uint20 表示后,前 10 位映射到[U+D800, U+DBFF],后 10 位映射到[U+DC00, U+DFFF];

小结: 定长编码的优点是转换规则简单直观,查找效率高,缺点是空间浪费,以及不可扩展。如果 Unicode 字符集进一步扩充,UTF-16 和 UTF-32 都将不可用,而 UTF-8 具有更强的可扩展性。

Golang 中字符编码

不像 C++、Java 等语言支持五花八门的字符编码,Golang 遵从“大道至简”的原则:全给老子用 UTF-8。所以 go 程序员再也不用担心乱码问题,甚至可以用汉字和表情包写代码,string 与字节数组转换也是直接转换,十分酸爽。

func TestTemp(t *testing.T) {
   
    来自打工人的问候()
}

func 来自打工人的问候() {
   
    问候语 := "早安,打工人😁"
    fmt.Println(问候语)
    bytes := []byte(问候语)
    fmt.Println(hex.EncodeToString(bytes))
}

// 执行结果-->
早安,打工人😁
e697a9e5ae89efbc8ce68993e5b7a5e4babaf09f9881

值得一提的是,Golang 中 string 的底层模型就是字节数组,所以类型转换过程中无需编解码。也因此,Golang 中 string 的底层模型是字节数组,其长度并非字符数,而是对应字节数。如果要取字符数,需要先将字符串转换为字符数组。字符类型(rune)实际上是 int32 的别名,即用 UTF-32 编码表示字符

func TestTemp(t *testing.T) {
   
    fmt.Println(len("早")) // 3
    fmt.Println(len([]byte("早"))) // 3
    fmt.Println(len([]rune("早")) // 1
}

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

再看一下 go 中 utf-8 编码的具体实现。首先获取字符的码点值,然后根据范围判断字节数,根据对应格式生成编码值。如果是无效的码点值,或码点值位于空段,则返回U+FFFD(即 �)。解码过程不再赘述。

// EncodeRune writes into p (which must be large enough) the UTF-8 encoding of the rune.
// It returns the number of bytes written.
func EncodeRune(p []byte, r rune) int {
   
    // Negative values are erroneous. Making it unsigned addresses the problem.
    
最低0.47元/天 解锁文章
HaloTech瑶光栈
关注 关注
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重构实战案例一,以子类取代类型编码
03-04
因此改进设计的一个重要方向就是消除重复代码(DuplicateCode)2.2「重构」使软件更易被理解你的源码还有其它读者:数个月之后可能会有另一位程序员尝试读懂你的代码并做一些修改。我们很容易忘记这第二位读者,但他...
各种编码UNICODE、UTF-8、ANSI、ASCII、GB2312、GBK详解
06-11
各种编码UNICODE、UTF-8、ANSI、ASCII、GB2312、GBK详解
程序员真的“编码太多没时间思考”了吗
热门推荐
文斌咨询
02-28 1万+
如题,这是有朋友问我的一个问题,我觉得这个问题也代表了一部分程序员的迷惑,所以不妨在此啰嗦几句。        我们都知道,目前程序员的工作强度很高,压力很大,白天8小时是不够的,加班肯定是家常便饭,整个人几乎全部的时间都用在了编码上。于是才产生了这个疑问,如果我整天都在机械的编码,那么我如何才能思考问题、获得提高呢?        我认为首先要明确的一点是编码与思考这两项活动不是串行的,不是说时
一个例子搞懂编码问题
小布丁的读书笔记
06-21 7589
0x00 前言 相信中文字符编码问题每个程序员都或多或少遇到过,文件读写、网页抓取、数据库等等,凡是有中文的地方总会出现各种的编码问题,但是很少有人愿意花时间去彻底弄清这个问题(至少我以前是这样),每次出现乱码问题的时候上网一搜,不能解决的一愁莫展,能够解决的也不知其所以然。 最近在学习Python的过程中再次遇到了这个问题,决定认认真真把编码问题搞清楚,同时也把经验和心得分享给大家。
字符编码的简单实例
xiueer
10-13 545
package encoding;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter;public class EncodeStre
一个简单的解码编码实例
iteye_16733的博客
09-04 428
[code="java"]import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; imp...
1800个程序员必备词汇-开发必备-适用前后端-编程词汇-1800词40页高清完整版-带音标-右侧下载前可预览.pdf
12-24
这份资料名为"1800个程序员必备词汇-开发必备-适用前后端-编程词汇-1800词40页高清完整版-带音标-右侧下载前可预览.pdf",是一份专为程序员设计的英语词汇手册,包含了大约1800个在软件开发中常用的词汇,并附带音标...
程序员必备
07-22
资源名称:程序员必备 资源太大,传百度网盘了,链接在附件中,有需要的同学自取。
用例子详细介绍各种字符集编码转换问题
北海-叶明的专栏
12-18 5879
---本文背景:本人在编程时需要匹配字符串,由此想到了如果文件是各种字符编码的话,匹配结果有可能不正确,那么,如何判断不同的字符集?如何在不同字符集之间做转换?对于UNICODE编码逐渐通用的情况下,我们软件人员如何从容应对? 本文首先对常用字符集进行总结,然后在字符集的显示及转换上以实例介绍,最后总结了编程中遇到的编码问题。本文目的:对字符集编码做详细介绍,关键配以实例讲
编码规范示例
12-25
/**************************************************/ /*名称:文件名称 /*描述:描述文件实现的功能 /*作成日期: /*参数: 参数1:参数名称、参数类型、输入/输出参数、参数含义 参数2:参数名称、参数类型、输入/输出参数、参数含义 参数3:参数名称、参数类型、输入/输出参数、参数含义 /*返回值:返回值名称、类型、含义 如果函数不需要返回值,本项填写“VOID”。 /*作者: /***************************************************/
程序员编码规范
09-12
满足Java开发的国家电网各种编码规范。
一些常见编码
weixin_44300286的博客
08-21 5057
前言: 学了那么久的编码,之前一直都没有时间整理,现在来总结一下。 1、base家族 1.1、base16 就是16进制,编码内容只有数字0-9,和大写英文字母A-F 1.2、base32 编码内容只有大写英文字母A-Z,和数字234567 1.3、base64 编码内容主要有大写英文字母A-Z,小写英文字母a-z,数字0-9,+和/ 2、凯撒密码 凯撒密码主要通过偏移来实现加密,偏移量是几,密...
各种编码
NO.one的专栏
09-26 1360
编码定义用预先规定的方法将文字、数字或其他对象编成数码,或将信息、数据转换成规定的电脉冲信号。编码在电子计算机、电视、遥控和通讯等方面广泛使用。  编码是根据一定的协议或格式把模拟信息转换成比特流的过程。在计算机硬件中,编码(coding)是在一个主题或单元上为数据存储,管理和分析的目的而转换信息为编码值(典型地如数字)的过程。在软件中,编码意味着逻辑地使用一个特定的语言如C
python编码示例_Python中字符串与编码示例代码
weixin_30093045的博客
02-21 233
在最新的python 3版本中,字符串是以unicode编码的,即python的字符串支持多语言编码和解码字符串在内存中以unicode表示,在操作字符串时,经常需要str和bytes互相转换如果在网络上传输或保存到磁盘上,则从内存读到的数据就是str,要把str变为以字节为单位的bytes,称为编码如果从网络或磁盘上读取字节流,则从网络或磁盘上读到的数据就是bytes,要把bytes变为str,...
各种编码汇总
yjreset的博客
02-15 443
一.url编码1.解释 url编码是一种浏览器用来打包表单输入的格式 2.编码规则 任何特殊的字符(就是那些不是简单的七位ASCII,如汉字)将以百分符%用十六进制编码,当然也包括象 =,&;,和 % 这些特殊的字符。其实url编码就是一个字符ascii码的十六进制。不过稍微有些变动,需要在前面加上“%”。比如“\”,它的ascii码是92,92的十六进制是5c,所以“\”的url编码就是%
几种常见编码
后台开发
08-27 1万+
ASCII码:美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII 码 Unicode:世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字都表示的,这是一种所有符号的编码 UTF-8:互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。UTF
常见编码方式(ASKII、GBK、UTF-8等)
道行的博客
02-20 7979
常见编码方式1.美国1.1 ASCII2. 西欧2.1 ISO-8859-12.2 windows-12523. 中国3.1 GB23123.2 GBK3.3 GB180304. 港澳台4.1 Big55. 世界通用5.1 Unicode5.2 UTF-325.3 UTF-165.4 UTF-86.总结 1.美国 1.1 ASCII 世界上虽然有各种各样的字符,但计算机发明之初没有考虑那么多,基本上只考虑了美国的需求。美国大概只需要128个字符,美国就规定了这128个字符的二进制表示方法,这个方法是一个标准
字符编码笔记:ASCII,Unicode和UTF-8(摘录)
今天的追求 && 明天的梦想
01-30 407
摘录自:   http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html  1. ASCII码我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示25
写文章

热门文章

  • 程序员必备:10分钟搞懂各种编码丨另附实战案例 2413
  • 你竟然是这样的人工智能? 788
  • 虚拟幻想的图灵机竟是人工智能的起源? 672
  • 游戏“头号玩家”:像做游戏一样做web开发 663
  • M1芯片制霸苹果生态?2021 年 Apple 春季新品发布会全记录 418

分类专栏

  • 笔记 1篇

大家在看

  • 基于python flask的高血压疾病预测分析与可视化系统的设计与实现,使用随机森林、决策树、逻辑回归、xgboost等机器学习库预测 519
  • 为啥要学AI?学会了能做什么?
  • npm ERR! Cannot read properties of null (reading ‘matches‘) npm ERR! A complete log of this run can
  • 银河麒麟桌面操作系统V10(SP1)离线升级SSH(OpenSSH)服务 1
  • 深入探秘:立项报告在项目管理中的精准定位与高级策略

最新文章

  • 你竟然是这样的人工智能?
  • M1芯片制霸苹果生态?2021 年 Apple 春季新品发布会全记录
  • 虚拟幻想的图灵机竟是人工智能的起源?
2021年7篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家玻璃钢雕塑人工成本占比三门峡玻璃钢商场美陈SMC玻璃钢雕塑商场春季美陈蝴蝶广西玻璃钢动物雕塑小猪摆件古铜玻璃钢雕塑价格重庆有没有做玻璃钢雕塑的日照景区玻璃钢雕塑厂家吉林环保玻璃钢雕塑供应商滁州美陈商场布置卢氏玻璃钢雕塑加工厂家北关玻璃钢花盆花器江门会发光的玻璃钢雕塑现货玻璃钢卡通雕塑定制价格园林风商场美陈济南商场美陈批发温州玻璃钢人像雕塑大旺玻璃钢雕塑价格阳江玻璃钢雕塑推荐厂家玉林玻璃钢泡沫雕塑厂家佛像玻璃钢雕塑定制厂家淮安商场户外美陈玻璃钢雕塑用什么颜料芜湖学校玻璃钢雕塑杭州玻璃钢花盆费用云南镂空玻璃钢雕塑北京超市商场美陈报价玻璃钢景观雕塑厂家定制甘南人物玻璃钢雕塑公司吉林火烈鸟玻璃钢雕塑制作香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化