浅谈一下前端字符编码

2023-09-17 10:47:38

背景

众所周知,计算机只能识别二进制,它是由逻辑电路组成,逻辑电路通常只有两个状态,开关的接通与断开,这两种状态正好可以用二进制数的0和1表示。但是现实中存在着其他的字符:数字、字母、中文、特殊符号等。因此就需要将这些字符转化成计算器可以识别的二进制编码。而我们在开发过程中,也常常会遇到各种各样的编码,例如ACSII、utf-8、base64等编码,接下来让我们来看一下这些常见编码。
编码方式

ASCII

我们知道在计算机存储数据时要使用二进制进行表示。而最初计算机只在美国使用,因此人们要考虑如何使用二进制来表达 52 个英文字母(包括大小写)、阿拉伯数字(0-9)以及常用的符号(如! @ # $ 等)。
于是便有从电报码发展而来的 ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)(发音 /ˈæski/)编码。它定义了英文字符和二进制的对应关系,一直沿用至今。

标准的ASCII字符总计有128个字符(2^7),字节的最高位一般设置为0。按照字符是否可见,可分为33个不可见字符,95个可见字符。

● 不可见字符:0-31 和 127 (0x00-0x1F 和 0x7F) 为不可见字符,也是控制字符,共 33 个。用于进行终端的换行、响铃、删除等动作。
在这里插入图片描述

● 可见字符:32-126 (0x20-0x7E) 为可见字符,共 95 个,存储了空格、0-9 十个阿拉伯数字、52 个大小写英文字母,以及标点、运算符号等。
在这里插入图片描述

虽然现代英语使用 128 个字符就足够了,但表示其他语言就远远不够了。因此当 ASCII 进入欧洲后,又被扩展为了 EASCII(Extended ASCII),将 7 bit 扩展为 8 bit,从128为扩展成256位,并且前 127 个编码含义和ASCII 保持一致。
编码
我们要知道一个字符对应的二进制,可以先找到它对应的十进制,然后再转化为二进制。
例如‘d’字符,它对应的十进制是100,转二进制的口诀是:除2倒取余法”,即将十进制整数除以2,得到一个商和一个余数;再将商除以2,又得到一个商和一个余数;以此类推,直到商等于零为止。
计算如下,倒取余数可以得到:110 0100
在这里插入图片描述

解码
可以看到以下的编码,使用 ASCII 码进行映射时,下面的二进制编码可以翻译成“Hello world”。

01001000 01100101 01101100 01101100 01101111 00100000 01110111 
01101111 01110010 01101100 01100100

我们列举一下第一个二进制的0100100的转化,对应十进制的转法是:把二进制数按权展开、相加即得十进制数。

2^6+2^3=64+8=72

可以算出它对应的十进制是72,从表格上对应到的字符就是H。

ASCII的主要缺点是它只能表示256个不同的字符,因为它只有8位。这意味着ASCII无法编码世界上许多其他语言中的字符。如果想要在计算机上使用中文、俄语、日语等语言,就需要另一种不同的字符编码标准。Unicode进一步扩展为UTF-8、UTF-16、UTF-32等编码方案,以便能够编码各种类型的字符。因此,ASCII和Unicode之间的主要区别在于所使用的位数进行编码。接下来我们来看一下Unicode的概念和使用方式。

Unicode

统一码(Unicode),也叫万国码、单一码它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、垮平台进行文本转换、处理的要求,是国际组织制定的,用于收纳世界上所有文字和符号的字符集方案。前128个字符同ASCII一样,进行扩充后,使用数字0-0x10FFFF来映射这些字符。

● 码点
Unicode 规定了每个字符的数字编号,这个编号被称为 码点(code point)。码点以 U+hex 的形式表示,U+是代表Unicode的前缀,而 hex 是一个16进制数。取值范围是从 U+0000 到 U+10FFFF。每个码点对应一个字符,绝大部分的常见字符在最前面的 65536 (2^16)个字符,范围是 U+0000到U+FFFF。

● 字符平面:目前的Unicode分成了17个编组,也称平面,每个平面有65536个码点。
○ 基本平面:U+0000 - U+FFFF,多数常见字符都在该区间,其他平面则为辅助平面。
○ 辅助平面:U+10000 到 U+10FFFF,如我们在网上常见 Emoji 表情。

Unicode通常为两个字节,对于英文字符的一个字节即可表示,高位字节补0,这样对比ASCII编码存储空间就会翻倍,在存储和传输上就十分不划算。这就会使得Unicode编码一时间很难推广。于是,为了较好的解决 Unicode 的编码问题, UTF-8 和 UTF-16、UTF-32 应运而生(UTF-8是8位的单字节码元,UTF-16是16位的双字节码元,UTF-32是32位的四字节码元)。UTF是Unicode TransferFormat的缩写。
Unicode和ASCII的区别如下:

UTF-8

UTF-8是一种可变长度字符编码,其第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。

计算机在读取 UTF-8 中以 0 开头的内容时,就知道只需要读取一个字节并显示 Unicode 中 0-127 范围内的正确字符即可。如果遇到两个 1,就需要读取 2 个字节,范围为128-2047,3 个 1 在一起表示需要读取三个字节。

十六进制二进制范围
0000 0000 - 0000 007F0xxxxxxx0-127
0000 0080 - 0000 07FF110xxxxx 10xxxxxx128-2047
0000 0800 - 0000 FFFF1110xxxx 10xxxxxx 10xxxxxx2048-65535
0001 0000 - 0010 FFFF1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx65536-2098151

应用

1. URL编码
在前端常接触的网页中,URL链接编码也是非常常见的。因为URL 只能包含标准的 ASCII 字符,所以必须对其他特殊字符进行编码。
JavaScript提供了四个URL的编码/解码方法,可以用于将非ASCII码的字符,如中文字符、特殊字符、表情字符等,进行UTF-8的编解码操作:
● 编码:encodeURI() 和 encodeURIComponent()
● 解码:decodeURI() 和 decodeURIComponent()

转换方式为:先转为UTF-8的字节码,然后前面加个 % 进行拼接得到编码结果。

encodeURI(' 12 33')--->'%2012%2033'
decodeURI('%2012%2033')--->' 12 33'

注意encodeURL有11个字符不能进行编码,只能使用encodeURLComponent进行编码

encodeURI与encodeURIComponent区别

○ encodeURI
encodeURI通常用于转码整个 URL,不会对URL 元字符以及语义字符进行转码,URL元字符:

  1. URL 元字符:分号(;),逗号(,),斜杠(/),问号(?),冒号(:),at(@),&,等号(=),加号(+),美元符号($),井号(#)
  2. 语义字符:a-z,A-Z,0-9,连词号(-),下划线(_),点(.),感叹号(!),波浪线(~),星号(*),单引号('),圆括号(())

○ encodeURIComponent
encodeURIComponent()通常只用于转码URL组成部分,如URL中?后的一串;会转码除了语义字符之外的所有字符,即元字符也会被转码

2. 指定编码
如果没有显式指定编码方式,浏览器假定任何程序的源代码都是用本地字符集编写的,这会因国家/地区而异,可能会出现意料之外的情况。因此,给 JavaScript 文档设置字符集非常重要,可以使用以下三种方式进行设定

○ 获取文件时,可以在Content-type指定

Content-Type: application/javascript; charset=utf-8

○ 在script标签设置charset

<script src="./app.js" charset="utf-8">

○ 嵌入head中


<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
</head>

在前端开发中,Javascript程序是使用Unicode字符集,Javascript源码文本通常是基于UTF-8编码。
但js代码中的字符串类型是UTF-16编码的,正如 ECMAScript 标准所说,JavaScript 字符串都是 UTF-16 序列。这也是解释了api接口返回字符串在前端出现乱码,因为多数服务都使用utf-8编码,前后编码方式不一致。

3. ‘锟斤拷’乱码问题
由于 Unicode 字符集在不断更新中,因此会出现 A 系统发送的字符,在 B 系统中无法识别的情况。于是 Unicode 规定对于无法识别的字符,一律使用 �(0xFFFD )字符来代替。
将FFFD转utf-8时,我们可以先将其转十进制等于65534,发现其在三个字节内的,因此先将其转成二进制。
从低位开始分成六位一组得到三组: 1111 111111 111110。后两组前面补10,第一组补1110。最后得到utf-8编码: 11101111 10111111 10111110。

//十六进制 -》十进制
FFFD -> 15*16^3+15*16^2+15*16+14=65534  -1111 111111 111110
                                        11101111 10111111 10111110
                                        	EF        BF       BD

然后再将其转成十六得到EF BF BD,即 0xFFFD 在 UTF-8 编码下为 0xEF 0xBF 0xBD,当多 � 出现时,就会产生连续的 0xEF 0xBF 0xBD 0xEF 0xBF 0xBD。
如果这些字符又被使用了 GB 编码的程序中打开,就会按照 GB 双字节编码将其解析。这样刚好就对应了 「0xEFBF 锟」 ,「0xBDEF 斤」,「0xBFBD 拷」 这几个字。
在这里插入图片描述

base64

Base64 也称为 Base64 内容传输编码。Base64 是将二进制数据编码为 ASCII 文本。Base64 一个字节只能表示 64 种情况,且编码格式每个字节的前两位都只能是 0,使用剩下的 6 位表示内容。再加上大多数字符集中存在的一个填充字符=。所以它是一种仅使用可打印字符表示二进制数据的方法。Base64 常用于在通常处理文本数据的场景,表示、传输、存储一些二进制数据,包括MIME的电子邮件及XML的一些复杂数据、以及图片地址。

这种编码格式无法充分利用存储资源,效能较低。那为什么还会成为网络中的普遍用法呢?
----其实 Base64 最早是应用在邮件传输协议中的。当时邮件传输协议只支持 ASCII 字符传递,使用 ASCII 码来表示所有的英文字符和数字还有一些符号。这里有一个问题,如果邮件中只传输英文数字等,那么 ASCII 可以直接支持。但是如果要在文件中传输图片、视频等资源的话,这些资源转成 ASCII 的时候会出现非英文数字的情况。而且邮件中还存在很多控制字符,这些控制字符又会成为不可见字符。非英文字符和控制字符在传输过程中很容易产生错误,影响邮件的正确传输。为此才有了诞生了一个新的编码规则,把二进制以 3 个字节为一组,再把每组的 3 个字节(24 位)转换成 4 个 6 位,每 6 位根据查以下映射表对应一个 ASCII 符号。不够6位使用000000 字节值在末尾补足,使其字节数能够被 3 整除,补位用 = 表示,每2个额外的0由1个 = 字符表示,并在解码时自动去除这就是 Base64。
在这里插入图片描述

编码
例如我们要编译hello,首先将其转成ASCII码01001000 01100101 01101100 01101100 01101111
Hello–> 01001000 01100101 01101100 01101100 01101111
然后将其从前往后,三个字节为一组,后面两个字节也自成一组。每一组按照六位为一组,不够六位补0。
算出每组对应的十进制,然后到表格中找出对应的符号,对应的转化如下,由于最后的一组补充了两个0,因此需要补充1个填充字符=

010010 000110 010101 101100           011011 000110 111100
18       6      21      44              27    6      60
S        G       V       s               b     G     8=

即编码后hello-》SGVsbG8=

解码
了解了编码,我们来看一下解码,还是用上面的例如对于编码后的字符SGVsbk8==。
四个字符为一组,并且删除每一组尾部的=。将每一个字符对应的十进制找到,然后再转化为二进制。从高到低每8位为一组,可得到:01001000 01100101 01101100 01101100 01101111对应ASCII表格对应的字符即可得到Hello

S        G         V        s                        b          k        8
18       6         21       44                       27         10       60
010010   000110   010101   101100                    011011    000110   111100
01001000  01100101  01101100                         01101100 01101111

应用

● javascript对应的base64编解码方法
在JavaScript 中,可以使用 btoa(binary to ASCII)和 atob(ASCII to binary)方法来做 Base64 的编码和解码。
例如对‘Hello’做 Base64 的编码与解码:
在这里插入图片描述
对于中文的base64编解码,由于ASCII 无法表示中文,因此要先做 UTF-8 编码,然后再做Base64 编码;解码方式为先做 Base64 解码,再做UTF-8 解码:

const encodedData = btoa(encodeURI('你好')); //  "JUU0JUJEJUEwJUU1JUE1JUJE"
const decodedData = decodeURI(atob(encodedData)); // "你好"

在这里插入图片描述

● base64图片地址
通常在图片比较多的情况为了减少http请求,图片地址我们会用base64编码。
前端拿到这个data字符串后,先拼接一下前缀:data:图片类型 ; 编码类型, data字符串数据
data:image/png;base64,iVBORw0KGgoAA…

有两种方式显示图片

  1. css方式-背景图片
img {
    background-image: url(data:image/png;base64,iVBORw0KG......);
}    
  1. img标签方式
<img width="900" height="450" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGMAAAAqCAYAAA...."/>

参考资料

聊聊前端字符编码:ASCII、Unicode、Base64、UTF-8、UTF-16、UTF-32-51CTO.COM
Base64 编码知识,一文打尽!
● 关于编码的那些事——前端应该了解的字符编码_winty~~的博客-CSDN博客
● 前端开发中需要搞懂的字符编码知识_前端的字符和字节_jh035的博客-CSDN博客

更多推荐

《Python趣味工具》——自制emoji(4)计算机二级考试题

前面我们学习了如何制作emoji,相信你也是有很多想法了吧!今天我们就来看看几道计算机二级考试真题。1.绘制套圈使用turtle库的circle()函数和seth()函数绘制套圈。最小的圆圈半径为10像素,不同圆圈之间的半径差是40像素。ps:注意要和题目要求的圆形方向一致哦~可以在绘制前先将方向调整为90度。示例代码

Kubernetes介绍(一)

kubernetes官网:Kubernetes1、Kubernetes是什么Kubernetes也称为K8s,是用于自动部署、扩缩和管理容器化应用程序的开源系统,k8s是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。Kubernetes可以实现以下功能:自动化容器的部署和复制

Swing程序设计详解(一)

【今日】“若你决定灿烂,山无遮,海无拦”目录初识Swing一Swing简述二Swing常用窗体2.1JFrame窗体2.2JDialog对话框2.3JOptionPane小型对话框(1)通知框(2)确认框(3)输入框(4)自定义对话框三常用布局管理器3.1绝对布局3.2流布局3.3边界布局3.4网格布局四常用面板4.1

如何实现不同MongoDB实例间的数据复制?

作为一种SchemaFree文档数据库,MongoDB因其灵活的数据模型,支撑业务快速迭代研发,广受开发者欢迎并被广泛使用。在企业使用MongoDB承载应用的过程中,会因为业务上云/跨云/下云/跨机房迁移/跨地域迁移、或数据库版本升级、数据库整合、数据库拆分、容灾等业务场景,存在MongoDB迁移或同步的业务诉求。在M

【CV、数据分析、AI产品经理】2024届校招岗位汇总

CV计算机视觉岗位数量公司岗位名称岗位职责截止日期1网易雷火人工智能算法工程师(虚拟交互/图形学和动画方向)岗位描述1.负责面向虚拟角色/虚拟人的创建(捏脸)和驱动(表情合成、动作捕捉和重定向、动作合成、舞蹈合成、物理模拟)等系统的设计和实现2.深度参与上述能力在游戏和泛娱乐产品(短视频、直播、虚拟偶像、VTuber)

【产品经理】深入B端SaaS产品设计核心理念

这几年各企业的B端业务都在做SaaS平台,但对SaaS的了解还不是完全全面,对于一些产品的定位以及设计还在探索中本文讨论“为什么采用SaaS模式”、“SaaS产品有哪些”以及“如何做好SaaS产品设计”三个话题,核心是产品设计,主要从需求定义、方案设计和开发交付3方面,共计讨论10个问题点。一、Why为什么要用SaaS

小谈设计模式(3)—策略模式

小谈设计模式(3)—策略模式专栏介绍专栏地址专栏介绍策略模式主要角色环境(Context)抽象策略(Strategy)具体策略(ConcreteStrategy)角色总结核心思想封装算法定义抽象策略使用环境类思想总结Java代码实现——以一个游戏角色攻击方式的例子首先,我们定义一个抽象策略类AttackStrategy

竞赛选题 基于机器视觉的手势检测和识别算法

0前言🔥优质竞赛项目系列,今天要分享的是基于深度学习的手势检测与识别算法该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!🧿更多资料,项目分享:https://gitee.com/dancheng-senior/postgraduate1实现效果废话不多说,先看看学长实现的效果吧2技术原理2.1手部检测主流的手势

Linux内核源码分析 (B.1)深入理解 Linux 虚拟内存管理

Linux内核源码分析(B.1)深入理解Linux虚拟内存管理文章目录Linux内核源码分析(B.1)深入理解Linux虚拟内存管理写在本文开始之前....1.到底什么是虚拟内存地址2.为什么要使用虚拟地址访问内存3.进程虚拟内存空间4\.Linux进程虚拟内存空间4.132位机器上进程虚拟内存空间分布4.264位机器

二刷力扣--二叉树(2)

226.翻转二叉树给你一棵二叉树的根节点root,翻转这棵二叉树,并返回其根节点。使用递归解决。确定函数参数和返回值函数参数为当前节点cur。无返回值。defdd(cur):确定终止条件。当前节点为空则终止。ifnotcur:return单层逻辑反转当前节点的左右,然后递归调用cur.left,cur.rightdef

企业中 Docker 的 Swarm 使用及作用详解

企业中Docker的Swarm使用及作用详解本文将详细介绍企业中Docker的Swarm使用及其在企业中的作用。通过使用Java代码示例,我们将演示Swarm的基本概念、创建Swarm集群以及部署和管理服务等操作。Docker的Swarm功能可帮助企业实现容器编排和集群管理,提供高可用性、可伸缩性和负载均衡等特性。在当

热文推荐