浅谈一下前端字符编码

news2025/1/15 6:47:15

背景

众所周知,计算机只能识别二进制,它是由逻辑电路组成,逻辑电路通常只有两个状态,开关的接通与断开,这两种状态正好可以用二进制数的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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1016618.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何实现Web应用、网站状态的监控?

如何实现Web应用、网站状态的监控&#xff1f; 关键词&#xff1a;网站监控,服务器监控,页面性能监控,用户体验监控本文通过代码分析、网站应用介绍网站状态监控的方式下文主要分为网站应用、技术实现两部分 一、网站应用 现在网络上已经存在一些Web网站监控的服务&#xff…

Vue ——08、路由嵌套,参数传递及重定向

路由嵌套&#xff0c;参数传递及重定向 一、路由嵌套二、参数传递第一种方式&#xff1a;第二种方式&#xff1a; 三、重定向————————创作不易&#xff0c;如觉不错&#xff0c;随手点赞&#xff0c;关注&#xff0c;收藏(*&#xffe3;︶&#xffe3;)&#xff0c;谢…

【C++杂货铺】继承由浅入深详细总结

文章目录 一、继承的概念及定义1.1 继承的概念1.2 继承定义1.2.1 定义格式1.2.2 继承方式和访问限定符1.2.3 继承基类成员访问方式的变化 二、基类和派生类对象赋值转换三、继承中的作用域四、派生类中的默认成员函数4.1 默认构造函数4.2 拷贝构造函数4.3 赋值运算符重载函数4.…

03使用Spring基于XML的方式注册第一个组件

基于XML的方式注册第一个组件 开发步骤 第一步: 创建Maven工程配置生成的pom.xml文件, 添加spring context基础依赖和junit依赖(注意根据Spring官方文档描述,Spring6需要JDK版本17) 当添加Spring的基础依赖spring context之后,Maven会自动关联并引入其他依赖spring aop, spr…

前端生态系统:构建现代Web应用的完整指南

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 前端开发生态系统是一个…

Selenium常见问题解析

1、元素定位失败&#xff1a; 在使用Selenium自动化测试时&#xff0c;最常见的问题之一是无法正确地定位元素&#xff0c;这可能导致后续操作失败。解决方法包括使用不同的定位方式&#xff08;如xpath、CSS selector、id等&#xff09;&#xff0c;等待页面加载完全后再进行…

[NLP] LLM---<训练中文LLama2(二)>扩充LLama2词表构建中文tokenization

使用SentencePiece的除了从0开始训练大模型的土豪和大公司外&#xff0c;大部分应该都是使用其为当前开源的大模型扩充词表&#xff0c;比如为LLama扩充通用中文词表&#xff08;通用中文词表&#xff0c;或者 垂直领域词表&#xff09;。 LLaMA 原生tokenizer词表中仅包含少量…

asp.net+sqlserver医院体检信息管理系统

一、源码描述 这是一款简洁十分美观的ASP.NETsqlserver源码&#xff0c;界面十分美观&#xff0c;功能也比较全面&#xff0c;比较适合 作为毕业设计、课程设计、使用&#xff0c;感兴趣的朋友可以下载看看哦 二、功能介绍 该源码功能十分的全面&#xff0c;具体介绍如下&…

PyTorch深度学习实战——基于ResNet模型实现猫狗分类

PyTorch深度学习实战——基于ResNet模型实现猫狗分类 0. 前言1. ResNet 架构2. 基于预训练 ResNet 模型实现猫狗分类相关链接 0. 前言 从 VGG11 到 VGG19&#xff0c;不同之处仅在于网络层数&#xff0c;一般来说&#xff0c;神经网络越深&#xff0c;它的准确率就越高。但并非…

Linux学习第12天:基于API函数的字符设备驱动开发:一字一符总见情

本节学习的内容主要为基于LinuxAPI函数的字符设备驱动的开发&#xff0c;还包括在驱动模块加载的时候如何自动创建设备节点。总结的脑图如下&#xff1a; 一、驱动原理 1.分配和释放设备号 申请设备号函数&#xff1a; int alloc_chrdev_region(dev_t *dev, unsigned basemin…

改进YOLOv5小目标检测:构建多尺度骨干和特征增强模块,提升小目标检测

构建多尺度骨干和特征增强模块,提升小目标检测 背景代码使用配置文件如下🔥🔥🔥 提升小目标检测,创新提升 🔥🔥🔥 测试在小目标数据集进行提点 👉👉👉: 新设计的创新想法,包含详细的代码和说明,具备有效的创新组合 🐤🐤🐤 1. 本文包含两个创新改…

SQL优化--count优化

select count(*) from tb_user ;在之前的测试中&#xff0c;我们发现&#xff0c;如果数据量很大&#xff0c;在执行count操作时&#xff0c;是非常耗时的。 MyISAM 引擎把一个表的总行数存在了磁盘上&#xff0c;因此执行 count(*) 的时候会直接返回这个 数&#xff0c;效率很…

档案管理系统设计与实现

摘 要 近年来&#xff0c;随着企业彼此间的竞争日趋激烈&#xff0c;信息技术在企业的发展中占据着越来越重要的地位。在企业的运输生产中&#xff0c;档案已成为企业运输经营中不可或缺的一部分&#xff0c;为管理者进行管理决策和进行各种经营活动提供了重要的依据&#xf…

前后端分离--Vue的入门基础版

目录 一.前后端分离 二.Vue的简介 三.Vue的入门案例 四.Vue的生命周期 一.前后端分离 前后端分离是一种软件架构模式&#xff0c;将应用程序的前端&#xff08;用户界面&#xff09;和后端&#xff08;数据处理和业务逻辑&#xff09;独立开发、独立部署。在前后端分离的架…

【数据结构】AVL树的删除(解析有点东西哦)

文章目录 前言一、普通二叉搜索树的删除1. 删除结点的左右结点都不为空2. 删除结点的左结点为空&#xff0c;右节点不为空3. 删除结点的右结点为空&#xff0c;左节点不为空4. 删除结点的左右结点都不为空 二、AVL树的删除1. 删除结点&#xff0c;整棵树的高度不变化1.1 parent…

RISV-V架构的寄存器介绍

1、RISC-V的通用寄存器 &#xff08;1&#xff09;在编写汇编代码时&#xff0c;使用寄存器的ABI名字&#xff0c;一般不直接使用寄存器的编号&#xff1b; &#xff08;2&#xff09;x0-x31是用来做整形运算的寄存器&#xff0c;f0-f31是用来做浮点数运算的寄存器&#xff1b;…

傅里叶变换应用 (01/2):频域和相位

一、说明 我努力理解傅里叶变换&#xff0c;直到我将这个概念映射到现实世界的直觉上。这是一系列技术性越来越强的解释中的第一篇文章。我希望直觉也能帮助你&#xff01; 二、傅里叶变换中频域简介 声音是一种机械波&#xff0c;是空气中的振动或其他介质。音符对应于波的频率…

【LeetCode75】第五十七题 电话号码的字母组合

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们按下的按键&#xff0c;让我们返回对应按键可能产生的所有可能。 这是一道很经典的递归题&#xff0c;我们首先先拿一个数组把每个…

day45:C++ day5,运算符重载剩余部分、静态成员、继承

#include <iostream> #include <cstring> #define pi 3.14 using namespace std;class Shape { protected:double round;double area; public://无参构造Shape():round(40),area(100){cout<<"Shape::无参构造函数&#xff0c;默认周长为40&#xff0c;面…

C语言入门Day_21 函数的使用

目录 前言&#xff1a; 1.变量作用域 2.代码执行顺序 3.易错点 4.思维导图 前言&#xff1a; 我们是先定义函数&#xff0c;再调用函数。完成了函数的定义以后&#xff0c;我们就可以开始调用函数了&#xff0c;让我们来回顾一下&#xff1a; 调用函数分为两部分&#…