将文本转化为baseb4格式(base64加密解密)

238 阅读17分钟

知识点1—进制转换

1. 10进制转换为其他进制

在Number原型链上存在方法toString方法,用于将10进制转换为其他进制(2-36) 语法

// 指定要用于数字到字符串的转换的基数 (从 2 到 36)。如果未指定 radix 参数,则默认值为 10
num.toString([radix])

tips: 在使用数字调用方法时,建议加(),不然数字后面的点会被认为是小数点而不是方法调用。

返回值:对应进制的字符串

(33).toString() // '33'
(20005).toString(2) // '100111000100101'
(0x16).toString(16) // '16'

注: 若是没有标注,num默认为10进制, 以0x开始的数据表示16进制

2. 负数

Number原型链上的toString方法将10进制转化成其他进制尽是在num为正数时起作用

(-1000).toString(2) // '-1111101000' 但是实际的值为 0000011000 
3. 负数的十进制转化为2进制

负数在内存中是以补码的形式存储的,因此若是计算负数的二进制,需要先计算正值的二进制然后计算期补码;

计算补码: 先对二进制值逐位取反再对取反后的二进制数加1得到补码;

举例说明:-100转化为二进制

  • [1] 100转化为二进制为 1100100
  • [2] 逐位取反 0011011
  • [3] 加1 0011111
  • -100的二进制为 0011111
4. 其他进制转化为10进制

其他进制转换为10进制的规则为:按权展开相加

5. 16进制转化为10进制
  • 在16进制中ABCDEF表示的分别是10,11,12,13,14,15;
  • 从右向左按权展开相加
  • eg:52E4转换为10进制过程为
52E4 = 5 * 10^0 + 2 * 10^1 + 14 * 10^2 + 4 * 10^3
          = 5+32+3584+16384
          = 20005
6. 2进制转化为10进制
  • 从右向左按权展开相加
  • eg:

知识点2—ASCII码与Unicode

1. ASCII码
  • 在计算机内部,所有信息都是以二进制(0,1)进行存储的。
  • 每一个二进制位(bit)有0和1两种状态
  1. 一个字节有8个二进制位,八个二进制位就可以组合出256种状态。
  2. 也就是说,一个字节一共可以用来表示256种不同的状态。
  • 美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码,一直沿用至今。
  1. ASCII 码一共规定了127个字符的编码,比如空格space是32(二进制00100000),大写的字母A是65(二进制01000001)。
  2. 这127个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0。
  3. 以下这些编号呢就被称为码点,码点可以称为这些字符的身份证号码

image.png

2. 非ASCII
  • 英语用127个符号编码就够了,但是用来表示其他语言,127个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。

  • 于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。

  • 这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。

  • 但是,这里又出现了新的问题 -> 不同的国家有不同的字母,因此,哪怕它们都使用256个符号的编码方式,代表的字母却不一样。比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (ג),在俄语编码中又会代表另一个符号。

  • 但是不管怎样,所有这些编码方式中,0--127表示的符号是一样的,不一样的只是128--255的这一段。

  • 至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是 GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536 个符号。

3. Unicode

正如上一节所说,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。

可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是 Unicode,就像它的名字都表示的,这是一种所有符号的编码被称为万国码。

tips:知道为什么 txt文件使用vscode打开会乱码吗?
       原因:vscode默认使用的编码方式为utf-8而txt文件的编码方式为 GB2312编码格式,因此会乱吗;
       解决:若是想txt文件在vscode打开,在当前页面修改编码方式
4. unicode获取码点与对应的字符

fromCharCode

formCharCode是String的静态方法,用于将 Unicode 编码转为一个字符!

语法:String.fromCharCode(num1[, ...[, numN]])

  • num1, …, numN : 一个或多个 Unicode 值,即要创建的字符串中的字符的 Unicode 编码 (范围介于0~65535,因为0~65535是unicode的码点,超出就没有意义了)

返回值:返回一个长度为 N 的代表 Unicode 编码的字符;

举例说明:

String.fromCharCode(65) // 'A'
String.fromCharCode(25105) // '我'

charCodeAt

语法:string.charCodeAt(index)

返回值:指定 index 处字符 对应的Unicode码点(取值范围为0-65535)

UTF-32(不推荐)

UTF-32统一使用4个字节表示一个字符,虽然这样可以使得全世界使用统一的编码方式,但是占用存储空间,通信效率变低。

比如说ASCII码, 每个ASCII码表示的字符都需要在前面3个字符补足0才行;及时时中文BGK表示的字符前面都需要使用2个字符补足0才行,这降低了通信效率,因此不被采纳。

UTF-8(普遍使用)

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。

UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

UTF-8 的编码规则很简单,只有二条:

1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

下表总结了编码规则,字母x表示可用编码的位。

image.png 跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

下面,还是以汉字我为例,演示如何实现 UTF-8 编码。

我的Unicode为25105,转换为十六进制为6211(处于第三行),转换为二进制为 110001000010001;因此我使用UTF-8编码应该是

从右向左按照6位一组进行划分 110 001000 010001
划分完毕之后添加到 1110xxxx 10xxxxxx 10xxxxxx中去,若是空却使用0补齐
结果为 11100110 10001000 10010001
转换为16进制为 E68891

知识点3—URL编码/解码

[1] 为什么要进行URL编码

先看如下案例

  • 发送请求去获取数据,请求的url如下
https://www.baidu.com?wd=hello word&name=超超&age=18&sex=女

但是在浏览器打开,实际请求的url变为如下

https://www.baidu.com?wd=hello%20word&name=%E8%B6%85%E8%B6%85&age=18&sex=%E5%A5%B3
  • 为什么url会自己发生改变呢?
  1. 这是由于在网络请求中,只能采用A-Z a-z 0-9 以及字符 - _ . ! ~ * ' ( )这些字符,若是遇到汉字或一些特殊字符,就会先进行URL编码再发送请求。
  • 看到这里或许会觉得这也无所谓,只要在接收到url之后再进行解码即可!但是问题在于标准国际组织并没有规定具体的编码方法,而是将决定权交给了浏览器,也就是说不同的浏览器的编码方式可能会不同,那在解码时就需要进行判断比较麻烦 —> 若是使用js在发送请求之前对URL进行编码就实现了编码方式的统一!

[2] encodeURIComponent方法

  • 作用:encodeURIComponent函数可把字符串作为 URI 组件进行编码

  • 语法:encodeURIComponent(str);

  • 编译过程:该方法不会对 ASCII 字母和数字进行编码,也不会对这些 安全的ASCII 标点符号(- _ . ! ~ * ’ ( ))进行编码,只会对特殊字符进行编码。

    1. 安全ASCII字符:保留;
    2. 非安全ASCII字符:使用utf-8对其进行编码得到对应的字节,然后对每个字节进行百分号编码(将16进制的0x替换为%)

举例说明:

// ‘中文’转化为utf-8为 0xE4 0xB8 0xAD 0xE6 0x96 0x87, 进行百分号编码最终结果为 %E4 %B8 %AD %E6 %96 %87
encodeURIComponent('中文') // '%E4%B8%AD%E6%96%87'

举例说明

// 不会对数字与英文字符进行编码
encodeURIComponent("156xxxxxxxx") // '156xxxxxxxx'
// 会对特殊字符进行编码
 encodeURIComponent("hello word") // 'hello%20word'
// 会对非AscII码进行编码
 encodeURIComponent("中国真棒") // '%E4%B8%AD%E5%9B%BD%E7%9C%9F%E6%A3%92'
  • 使用场景:
    1. 在使用url进行参数传递的时候,当参数出现空格这样的特殊字段,后台只可以读取到空格前的内容,后面内容丢失,造成数据读取失败; 但是如果用encodeURIComponent函数将这些特殊字符进行转义,后台就可以成功读取传递的数据了;
    2. 用于base编码过程;

[3] decodeURIComponent

decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码

举例说明:

//'%E4%B8%AD%E5%9B%BD%E7%9C%9F%E6%A3%92'
encodeURIComponent('中国真棒') 
// '中国真棒'
decodeURIComponent('%E4%B8%AD%E5%9B%BD%E7%9C%9F%E6%A3%92')

知识点4—base64编码/解码

1. base64定义

所谓Base64,就是选出64个字符

  • 小写字母a-z、
  • 大写字母A-Z、
  • 数字0-9、
  • 符号"+“、”/"
  • 再加上作为垫字的"="

实际上是65个字符作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。 使得二进制数据在解释成 radix-64 的表现形式后能够用 ASCII 字符串的格式表示出来。

2. base64转换规则

base64转换规则具体来说可以分为四步:

  • 第一步,将每三个字节作为一组,一共是24个二进制位。
  • 第二步,将这24个二进制位分为四组,每个组有6个二进制位。
  • 第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节。
  • 第四步,根据下表,得到扩展后的每个字节的对应符号,这就是Base64的编码值。
  0 A  17 R   34 i   51 z

  1 B  18 S   35 j   52 0

  2 C  19 T   36 k   53 1

  3 D  20 U   37 l   54 2

  4 E  21 V   38 m   55 3

  5 F  22 W   39 n   56 4

  6 G  23 X   40 o   57 5

  7 H  24 Y   41 p   58 6

  8 I   25 Z   42 q   59 7

  9 J  26 a   43 r   60 8

  10 K  27 b   44 s   61 9

  11 L  28 c   45 t   62 +

  12 M  29 d   46 u   63 /

  13 N  30 e   47 v

  14 O  31 f   48 w   

  15 P  32 g   49 x

  16 Q  33 h   50 y

因为,Base64将三个字节转化成四个字节,因此Base64编码后的文本,会比原文本大出三分之一左右。

3. 举例说明

举一个具体的实例,演示英语单词Man如何转成Base64编码。

image.png

  • 第一步,“M”、“a”、"n"的ASCII值分别是77、97、110,对应的二进制值是01001101、01100001、01101110,将它们连成一个24位的二进制字符串010011010110000101101110。
  • 第二步,将这个24位的二进制字符串分成4组,每组6个二进制位:010011、010110、000101、101110。
  • 第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节:00010011、00010110、00000101、00101110。它们的十进制值分别是19、22、5、46。
  • 第四步,根据上表,得到每个值对应Base64编码,即T、W、F、u。

因此,Man的Base64编码就是TWFu。

4. 字节数不足三的情况

如果字节数不足三,则这样处理:

  • a)二个字节的情况:将这二个字节的一共16个二进制位,按照上面的规则,转成三组,最后一组除了前面加两个0以外,后面也要加两个0。这样得到一个三位的Base64编码,再在末尾补上一个"="号。
    1. 比如,“Ma"这个字符串是两个字节,可以转化成三组00010011、00010110、00010000以后,对应Base64值分别为T、W、E,再补上一个”="号,因此"Ma"的Base64编码就是TWE=。
  • b)一个字节的情况:将这一个字节的8个二进制位,按照上面的规则转成二组,最后一组除了前面加二个0以外,后面再加4个0。这样得到一个二位的Base64编码,再在末尾补上两个"="号。
    1. 比如,“M"这个字母是一个字节,可以转化为二组00010011、00010000,对应的Base64值分别为T、Q,再补上二个”="号,因此"M"的Base64编码就是TQ==。
5. base64编码对应的方法

btoa

btoa是binary to ascii的缩写;

作用:btoa是window对象的方法,作用是将二进制数据或ASCII字符串转换成一个base64编码过的ASCII字符串。

语法:btao(stringToEncode)

  • stringToEncode 是一个需要编码的二进制字符串;

  • 传入的字符串将视为一个二进制字符串 -> 将字符串中的每一个字节都视为一个二进制数据字节;

  • 因此:每个字符只能使用一个字节表示,若是其中包含了需要使用超过一个字节才能表示的字符,你就会得到一个错误,因为这个字符串不能被看作是二进制数据;

  • 因此btoa能够编码(全部的)ASCII字符却不能编码(超过一个字节的)Unicode字符。

    举例说明:

    1. ASCII字符串能够正常编码;
var str = "hello world";
var demo = btoa(str);
console.log(demo);// aGVsbG8gd29ybGQ=
  1. Uniconde字符串中存在超过1个字节的字符则不能正常编码,会报如下错误:
var str = "中国真棒";
var demo = btoa(str);
console.log(demo);

image.png

若是想要将Unicode字符编码为base64,在编码之前还需要使用encodeURIComponent方法将Unicode字符进行转换,再进行base64编码;

atob

atob:是ascii to binary的缩写

作用: atob是window的方法,作用是对经过 base-64 编码的字符串进行解码

var demo = "aGVsbG8gd29ybGQ=";
var str = atob(demo);
console.log(str);//hello world

若是在加密过程中使用了encodeURIComponent方法进行编码就要在解码过程中使用decodeURIComponent进行解码

重点-base64编码解码方法封装

js-base64编码

encode (str) {
  return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
    function toSolidBytes (match, p1) {
      return String.fromCharCode('0x' + p1)
  }))
}

js-base64解码

decode (str) {
  return decodeURIComponent(atob(str).split('').map(function (c) {
     return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
  }).join(''))
}

插件-js-base64

该插件的原理就是上述base64编码解码过程

安装:npm install --save js-base64

引入:

import { encode, decode } from 'js-base64';

使用

// 加密
encode(encodeURIComponent('张三')) // '5byg5LiJ'
// 解密
decode(decodeURIComponent('5byg5LiJ')) // 张三

六个方法的综合应用(插件的原理)

encode (str) {
  return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
    function toSolidBytes (match, p1) {
      return String.fromCharCode('0x' + p1)
  }))
}
  • [1] 对“中国真棒”进行base64进行编码
btoa(“中国真棒”) 
// 报错-> 不能对超过1个字节的字符进行编码
  • [2]将“中国真棒”拆解为多个单字节的字符串

    • [2.1]将字符编译为单个字节的字符

      encodeURIComponent('中国真棒')  //  '%E4%B8%AD%E5%9B%BD%E7%9C%9F%E6%A3%92'
      
    • [2.2]将编译后的字符转换为Unicode

      '%E4%B8%AD%E5%9B%BD%E7%9C%9F%E6%A3%92'.replace(
        /%([0-9A-F]{2})/g,
        function toSolidBytes(match,p1){
          return String.fromCharCode('0x' + p1) // 将UTF-16的数转化为对应的Unicode 编码的字符
        }
      ) // '中å\x9B½ç\x9C\x9Fæ£\x92'
      
  • [3] 将单子节自符进行base 64编码

btoa('中å\x9B½ç\x9C\x9Fæ£\x92') //'5Lit5Zu955yf5qOS'
  • [4] 对btoa方法编码后的字符进行解码
atob('5Lit5Zu955yf5qOS') // '中å\x9B½ç\x9C\x9Fæ£\x92'
  • [5] 将单字节字符还原-》还原为十六进制的转义序列
'中å\x9B½ç\x9C\x9Fæ£\x92'.split('').map(item => '%'+('00'+item.charCodeAt(0).toString(16)).slice(-2)).join('') 
// '%e4%b8%ad%e5%9b%bd%e7%9c%9f%e6%a3%92'
  • [6] 由于改字符使用过 encodeURIComponent编码过,因此使用decodeURIComponent进行解码
decodeURIComponent('%e4%b8%ad%e5%9b%bd%e7%9c%9f%e6%a3%92') // '中国真棒'