1.定义
| 概念 | 举例 | |
|---|---|---|
| 位bit | 计算机中所有的数据在存储和运算时都使用二进制数表示(用高电平和低电平分别表示1和0)。 所以bit是数据存储的最小单位。每个二进制数字0或者1就是1个位 | 0, 1 |
| 字节byte | 计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。 1 byte = 8 bit 1 KB = 1024 byte 1 MB = 1024 KB | 11111000,00000001,00000101 …… |
| 字符 | 人们使用的记号,抽象意义上的一个符号。 | '1', '中', 'a', '$', '¥', …… |
| 编码 | 具体用哪些二进制数字表示哪个字符,每个人都可以约定自己的一套(这就叫编码) | ASCII (American Standard Code for Information Interchange),ISO-8859-1,GB2312,GBK,UTF-8,UTF-16 |
2.编码的发展
| 系统内码 | 说明 | 系统 | |
|---|---|---|---|
| 阶段一 | ASCII | 计算机刚开始只支持英语,其它语言不能够在计算机上存储和显示 | 英文 DOS |
| 阶段二 | ANSI编码 (本地化) | 为使计算机支持更多语言,通常使用 0x80~0xFF (10000000 ~ 11111111)范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。 不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种延伸编码方式,称为 ANSI 编码。 在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。 | 中文 DOS,中文 Windows 95/98,日文 Windows 95/98 |
| 阶段三 | UNICODE (国际化) | 为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集;包含了各种语言中使用到的所有“字符”。用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。 官网:home.unicode.org/ 版本信息:www.unicode.org/history/pub… (目前最新的Unicode版本13.0中,包含的字符总数已达143859个) | Windows NT/2000/XP,Linux,Java |
console.log('ñ' === 'ñ') //false
console.log('\xF1ana' === 'n\u0303') //false
// 题1 反转字符串
const str = '我昨天😀今天😭';
console.log(str.split('').reverse().join('')); //��天今��天昨我
console.log([...str].reverse().join('')); // 😭天今😀天昨我
console.log('😀'.split('')); //["�", "�"]
// 题2 unicode和汉字互相转换
const str = '门';
const code = str.charCodeAt(0);
const str2 = String.fromCharCode(38376);
console.log(code) // 38376
console.log(str2) // 门
//but
const str = '😀';
const code = str.charCodeAt(0);
const str2 = String.fromCharCode(55357);
console.log(code) // 55357
console.log(str2) // �
//solution: ES2015之后,JS提供了新的API来支持Unicode码位:
console.log(str.codePointAt(0)) //128512
console.log(String.fromCodePoint(128512)) //😀
// 题3 将字符串中的字符安unicode编码排序
const str='this is a string 759';
function sortStr(str){
const strlist = str.split('');
let tmp;
//bubble sort
for(var i=strlist.length-1; i>=1; i--){
for(var j=1; j<=i; j++){
//codePointAt
if(strlist[j-1].codePointAt(0)>strlist[j].codePointAt(0)){
tmp=strlist[j-1];
strlist[j-1]=strlist[j];
strlist[j]=tmp;
}
}
}
return strlist.join('');
}
console.log(sortStr(str)); //579aghiiinrssstt
增补字符的码位均超过16位范围。
ES5及之前的JS的Unicode相关API,只能以UTF-16来处理BMP的字符,所有字符串的操作都是基于16位编码单元。
因此,当😀这样的增补字符出现时,得到的结果就会与预期不符。
// Unicode 转义
//前缀\u + 码位的十六进制字符串 表示一个字符
console.log('\u4e2d\u6587'); // 中文;
const \u4e2d\u6587 = '测试';
console.log(中文); // 测试
// \u+十六进制字符的这种表示法同样只适用于BMP的字符,所以如果我们试图使用它转义增补字符,直接这样是不行的:
console.log('\u1f004'); // 4
//引擎会把\u1f004解析成字符 \u1f00和阿拉伯数字4组成的字符串。我们需要使用{}将编码包含起来,这样就可以了:
console.log('\u{1f004}'); // 🀄
// 长度问题
console.log( '𝒳'.length ); // ?
console.log( '😂'.length ); // ?
console.log( '𩷶'.length ); // ?
console.log('ñ'.length); // ?
(JS字符串的length只能获得UTF-16字符的个数)
// 要获得Unicode字符数,有几个办法
//1.normalize()方法将根据指定的形式返回标准化的字符串(如果未设置参数,则NFC为默认值)。
//Unicode具有四种主要的规范化形式 NFC , NFD , NFKC , NFKD
console.log('ñ'.normalize().length); // 1
const str = '😀'
//2.使用spread操作是可以支持Unicode字符串转数组的,所以:
console.log([...str].length); // 1
//3.或者使用带有u描述符的正则表达式:
function getCodePointCount(str) {
let result = str.match(/./gu);
return result ? result.length : 0;
}
console.log(getCodePointCount(str));
//js和安卓上一个emoji都算作2个字符长度,而在ios上为1个字符长度。
2.编码的区别
| 说明 | 数量 | |
|---|---|---|
| ASCII | 0-31用于表示控制字符 如换行,回车,删除等 ASCII码的大小规则:0-9 < A-Z < a-z。 几个常见字母的ASCII码大小: A-65,a-97,0-48 | 128个 |
| ISO-8859-1 | 该编码是在ASCII编码的基础上扩展出来的,但它仍然是单字节编码 | 256个 |
| GB2312 | GB2312的全称是《信息技术 中文编码字符集》,它是双字节编码,编码范围是A1-F7 | 其中A1-A9是符号区,总共包含682个符号 B0-F7是汉字区,包含6763个汉字 |
| GBK | GBK的全称是《汉字内码扩展规范》,它的出现是为了扩展GB2312,它的编码范围是8140-FEFE(去掉XX7F),和GB2312兼容 | 23940个码位,能表示21003个汉字 |
| UTF-16 | 全新的超语言字典,世界上所有的语言都可以通过这个字典来相互翻译。用两个字节来表示Unicode的转化格式(两个字节就是16个bit,所以要UTF-16) | - |
| UTF-8 | UTF-8采用了一种变长技术,每个编码区域有不同的字码长度,不同类型的字符可以由1-6个字节组成 | - |
- UTF-16与UTF-8都是处理Unicode编码,它们的编码规则不太相同:
相对来说,UTF-16编码效率最高,字符到字节相互转换更简单,进行字符串操作也更好。它适合在本地磁盘和内存之间使用,可以进行字符和字节之间的快速切换,如Java的内存编码就采用UTF-16编码。但是它不适合在网络之间传输,因为网络传输容易损坏字节流,一旦字节流损坏将很难恢复。
相比较而言UTF-8更适合网络传输,对ASCII字符采用单字节存储,另外单个字符损坏也不会影响后面的其他字符,在编码效率上介于GBK和UTF-16之间,所以UTF-8在编码效率上和编码安全性上做了平衡,是理想的中文编码方式。(一个英文字为一个字节,一个中文为三个字节。)
• 页面乱码:
< meta http-equiv="Content-Type" content="text/html; charset=utf-8">
• 引用JS乱码:
<script charset="utf-8" src="xxx.js"></script>
• 编码和解码
escape() 使用转义序列替换某些字符来对字符串进行编码
unescape() 对使用 escape() 编码的字符串进行解码
const s = escape("JavaScript 中国");
console.log(s); // JavaScript%u4E2D%u56FD
console.log(unescape(s)); // JavaScript 中国
encodeURI() 通过转义某些字符对 URI 进行编码
decodeURI() 对使用 encodeURI() 方法编码的字符串进行解码
*相对而言,encodeURI() 方法更佳安全。它能够将字符转换为 UTF-8 编码字符,然后用十六进制的转义序列(形式为%xx)对生成的 1 字节、2 字节或 4 字节的字符进行编码。
const s = encodeURI("JavaScript 中国");
console.log(s); // JavaScript%20%E4%B8%AD%E5%9B%BD
console.log(decodeURI(s)); // JavaScript 中国
encodeURIComponent() 通过某些转义字符对 URI 的组件进行编码
decodeURICompoent() 对使用 encodeURIComponent() 方法编码的字符串进行解码
encodeURICompoent() 方法假定参数是 URI 的一部分,例如,协议、主机名、路径或查询字符串。因此,它将转义用于分隔 URI 各个部分的标点符号。而 encodeURI() 方法仅把它们视为普通的 ASCII 字符,并没有转换。
const s = "xx.taobao.com/abcd/xxx.html?keyword=URI";
const a = encodeURI(s);
console.log(a);
const b = encodeURIComponent(s);
console.log(b);
// xx.taobao.com/abcd/xxx.html?keyword=URI
// xx.taobao.com%2Fabcd%2Fxxx.html%3Fkeyword%3DURI
const c = decodeURIComponent(b);
console.log(c); //xx.taobao.com/abcd/xxx.html?keyword=URI