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个字节存储。

相关推荐
希望永不加班15 分钟前
Spring AOP 代理模式:CGLIB 与 JDK 动态代理区别
java·开发语言·后端·spring·代理模式
浮游本尊1 小时前
一次合同同步背后的多阶段流水线:从外部主数据到本地歧义消解
后端
lv__pf1 小时前
springboot原理
java·spring boot·后端
段小二2 小时前
服务一重启全丢了——Spring AI Alibaba Agent 三层持久化完整方案
java·后端
UIUV2 小时前
Go语言入门到精通学习笔记
后端·go·编程语言
lizhongxuan2 小时前
开发 Agent 的坑
后端
段小二2 小时前
Agent 自动把机票改错了,推理完全正确——这才是真正的风险
java·后端
itjinyin3 小时前
ShardingSphere-jdbc 5.5.0 + spring boot 基础配置 - 实战篇
java·spring boot·后端
Victor3563 小时前
MongoDB(91)如何在MongoDB中使用TTL索引?
后端