5分钟彻底弄明白ascii、unicode和utf-8

我们经常会听到ascii、unicode、utf-8,对于这些名词概念总是很模糊,似懂非懂,大部分时候不会遇到问题,即使遇到问题百度一下也能解决。其实花几分钟弄清楚这些概念其实很容易,让知识变得具体,下次遇到字符问题,就可以做到心中有数

什么是ASCII和Unicode?

ASCII和Unicode都是字符集,说简单点,就是一个表格,规定97对应英文字母"a",98对应英文字母"b",23383对应中文"字"等等,ASCII和Unicode的区别是Unicode这个"表格"更大

什么是utf-8?

utf-8是编码方式的一种,所谓的编码就是一种算法,字符集经过这种算法计算后,变成了另外的数字。比如英文字母a,经过utf-8编码后变成了\u0061,中文"字"经过utf-8编码后变成了\u5b57(十六进制)

深入了解

所以,ASCII和Unicode是字符集,而utf-8只是众多编码算法中的一种。接下来我们讨论几个问题,彻底弄明白这些概念

为什么会有ASCII?

ASCII全称是American Standard Code for Information Interchange,美国信息交换标准代码,计算最早是在美国被发明,为了能表示英文字母,就需要有一套处理字符的标准,于是就出现了ASCII

ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。

有了ASCII为什么还需要另一种字符集Unicode?

ASCII 编码是美国人给自己设计的,英文字母大写加小写总共52个,再算上数字和符号,1字节也足够了(1byte=8bit,最大能表示的数是255)。

于是欧洲那些扩展的拉丁字母,中文、韩语和日语都无法被加入ASCII。

各个国家为了让本国公民也能正常使用计算机,开始效仿 ASCII 开发自己的字符编码,例如 ISO/IEC 8859(欧洲字符集)、shift_Jis(日语字符集)、GBK(中文字符集)等。

这些字符集都有自己的编码,不同语言之间交流因为编码不同而出现乱码,为了解决这个问题就出现了Unicode字符集,Unicode 是国际标准字符集,它将世界各种语言的每个字符定义一个唯一的编码,字符所在码表的位置叫码点(code point),Unicode字符集的编码范围是 0x0000 - 0x10FFFF(0-1114111),可以容纳一百多万个字符

为什么需要utf-8?

unicode字符集只是规定了字符的二进制代码,却没有规定字符应该如何存储,最简单的方式就是直接存储字符对应的二进制(code point),这种编码方式叫UCS-4,UCS-4使用4个字节来储存1个字符。

但直接存储code point有一个问题:浪费空间,尤其是英文字母1字节就足够了,但用了4倍的空间来存储,造成了严重的空间浪费。

所以才出现了utf-8编码,他是变长编码方式,很好的解决了直接存储code point的问题

utf-8为什么变成了世界上最流行的编码?

先看看utf-8编码算法,他规定一个code point满足:

  • 0 ~ 127 --------> 使用0xxxxxxx 模板存储
  • 128 ~ 2047 -----> 使用110xxxxx 10xxxxxx 模板存储
  • 2048 ~ 65535 ---> 使用1110xxxx 10xxxxxx 10xxxxxx 模板存储
  • 65536 ~ 2097151-> 使用11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 模板存储

例如,汉字"字"utf-8编码过程:

查unicode中的code point23383

23383在2048 ~ 65535范围内,用1110xxxx 10xxxxxx 10xxxxxx模板。

23383转为二进制是1011_0110_1010_111

这个二进制不足16位,先补足16位:0101_1011_0101_0111

带入模板中:1110_0101 _10101101 _10010111

转为16进制:\xe5ad97。这就是utf-8编码的"字"

这样设计有什么好处?

不同范围的code point使用不同的字节存储,可以节省大量的空间

为什么开头要设计成那样的?

这是为了方便解码,从第一个bit开始

  • 如果是0,那后面的1字节(8bit)存储的1个字符
  • 如果非0,就看几个1开头,2个1开头,那后面2个字节(16bit)存储1个字符
  • 3个1开头,后面3个字节(24bit)存储1个字符
  • 4个1开头,后面4个字节(32bit)存储1个字符

那为什么从第二个字节开始,都是10开头呢?

这是为了保证在传输过程中出现中断,也能顺利的找到下一个字符的开头,只需要找到第一个非10开头的字节,就是下一个字符的开头了。

utf-8能否兼容ascii呢?

可以,因为0-127的存储方式0xxxxxxx正好就是ascii的存储方式。

以上这些原因使得utf-8成为了世界上最流行的编码。

动手实现一遍utf-8编码

js 复制代码
function codePointToUtf8(codePoint) { 
    var utf8Bytes = []; 
    var code = codePoint; 
    // 判断代码点的大小,确定使用多少个字节进行编码 
    if (code <= 0x7F) { 
        utf8Bytes.push(code); 
    } else if (code <= 0x7FF) { 
        utf8Bytes.push(0xC0 | (code >> 6)); 
        utf8Bytes.push(0x80 | (code & 0x3F)); 
    } else if (code <= 0xFFFF) { 
        utf8Bytes.push(0xE0 | (code >> 12)); 
        utf8Bytes.push(0x80 | ((code >> 6) & 0x3F)); 
        utf8Bytes.push(0x80 | (code & 0x3F)); 
    } else if (code <= 0x1FFFFF) { 
        utf8Bytes.push(0xF0 | (code >> 18)); 
        utf8Bytes.push(0x80 | ((code >> 12) & 0x3F)); 
        utf8Bytes.push(0x80 | ((code >> 6) & 0x3F)); 
        utf8Bytes.push(0x80 | (code & 0x3F)); 
    } else { 
        throw new Error("Invalid code point"); 
    } 
    // 返回字节数组 return utf8Bytes; 
} 
// 示例:将 Unicode 代码点 U+5B57 (中文字符"字") 转换为 UTF-8 
var utf8Array = codePointToUtf8(0x5B57); 
console.log(`UTF-8 encoding of "字" (U+5B57): ${utf8Array.map(byte => byte.toString(16)).join('')}`);
elixir 复制代码
defmodule Utf8Converter do
  def code_point_to_utf8(code_point) when code_point < 0x80 do
    <<code_point>>
  end

  def code_point_to_utf8(code_point) when code_point < 0x800 do
    <<0xC0 + (code_point >>> 6), 0x80 + (code_point & 0x3F)>>
  end

  def code_point_to_utf8(code_point) when code_point < 0x10000 do
    <<0xE0 + (code_point >>> 12),
      0x80 + ((code_point >>> 6) & 0x3F),
      0x80 + (code_point & 0x3F)>>
  end

  def code_point_to_utf8(code_point) do
    <<0xF0 + (code_point >>> 18),
      0x80 + ((code_point >>> 12) & 0x3F),
      0x80 + ((code_point >>> 6) & 0x3F),
      0x80 + (code_point & 0x3F)>>
  end
end

# 示例:将 Unicode 代码点 0x5B57 (中文字符"字") 转换为 UTF-8
binary = Utf8Converter.code_point_to_utf8(0x5B57)
IO.inspect binary

MySQL中的utf8和utf8mb4

MySQL旧版本中的utf8实际上不是真正的UTF-8编码, 因为他每个字符最多3个字节存储, 对于超过3个字节的字符就会出错,MySQL5.5.3开始引入utf8mb4,这才是真正的UTF-8编码,采用最多4个字节存储。

相关推荐
XINGTECODE33 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
程序猿进阶39 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺43 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
凡人的AI工具箱1 小时前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
先天牛马圣体1 小时前
如何提升大型AI模型的智能水平
后端
java亮小白19971 小时前
Spring循环依赖如何解决的?
java·后端·spring
2301_811274312 小时前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端
草莓base2 小时前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
Ljw...2 小时前
表的增删改查(MySQL)
数据库·后端·mysql·表的增删查改
编程重生之路2 小时前
Springboot启动异常 错误: 找不到或无法加载主类 xxx.Application异常
java·spring boot·后端