最近在用JS时,涉及到了不同编码之间的转换.于是汇总了一些网上的资料,整合成了自己的文档.
-备忘录
知识点涉及: 字符编码基础,不同编码之间的转换,如UTF-8转UTF-16,如UTF-16转GBK;以及Base64加密,GBK型与UTF-8型加密等.
注:
参考来源于网上资料
来源1: http://my.oschina.net/goal/blog/201032?fromerr=vEgm5b1A#OSC_h2_1
来源2: http://www.qqxiuzi.cn/zh/hanzi-gb2312-bianma.php
来源3: http://www.qqxiuzi.cn/zh/hanzi-gbk-bianma.php
来源4: http://www.qqxiuzi.cn/zh/hanzi-gb2312-bianma.php
来源5: http://www.qqxiuzi.cn/zh/hanzi-BIG5-bianma.php
来源6: http://www.fmddlmyy.cn/text6.html
来源7: http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html
来源8: http://www.jb51.net/article/62415.htm
(American Standand Code for InformationInterchange)的缩写
ASCII码是计算机最开始支持的基于拉丁字母的编码,一个字符用一个字节表示,只用了低7位,最高位为0,因此总共有128个ASCII码,范围为0~127。
0~127(总共有128个ASCII码),详情百度ASCII码表
适用于现代英文和其它西欧语言.
但是由于编码范围有限,所以无法支持多种地区的语言,后来就产生了各种其它的编码
方案
只占用一个字节
高位不占用,最多为(01111111)
即”标准码”,通常叫做Latin-1
ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号
0x00-0xFF(0-255)
此字符集支持部分于欧洲使用的语言,包括阿尔巴尼亚语、巴斯克语、布列塔尼语、加泰罗尼亚语、丹麦语、荷兰语、法罗语、弗里西语、加利西亚语、德语、格陵兰语、冰岛语、爱尔兰盖尔语、意大利语、拉丁语、卢森堡语、挪威语、葡萄牙语、里托罗曼斯语、苏格兰盖尔语、西班牙语及瑞典语。
但是不支持中文和东亚等国的语言.
只占用一个字节
高位可以用,最多为(11111111)
GB是”国标”两字的拼音首字,2312是标准序号
GB2312编码是第一个汉字编码国家标准,由中国国家标准总局1980年发布,1981年5月1日开始使用。GB2312编码共收录汉字6763个,其中一级汉字3755个,二级汉字3008个。同时,GB2312编码收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。
(GB2312中的字符是全角字符,ASCII原始的字符是半角字符)
A1A1-FEFE(41377-65278)
其中汉字编码范围:B0A1-F7FE(45217-63486)。
基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率。但对于人名、古汉语等方面出现的罕用字和繁体字,GB 2312不能处理,因此后来GBK及GB 18030汉字字符集相继出现以解决这些问题
中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312
GB2312编码对所收录字符进行了“分区”处理,共94个区,每区含有94个位,共8836个码位。这种表示方式也称为区位码。
01-09区收录除汉字外的682个字符。
10-15区为空白区,没有使用。
16-55区收录3755个一级汉字,按拼音排序。
56-87区收录3008个二级汉字,按部首/笔画排序。
88-94区为空白区,没有使用。
举例来说,“啊”字是GB2312编码中的第一个汉字,它位于16区的01位,所以它的区位码就是1601
GB2312规定对收录的每个字符采用两个字节表示,第一个字节为“高字节”,对应94个区;第二个字节为“低字节”,对应94个位。所以它的区位码范围是:0101-9494。区号和位号分别加上0xA0就是GB2312编码。例如最后一个码位是9494,区号和位号分别转换成十六进制是5E5E,0x5E+0xA0=0xFE,所以该码位的GB2312编码是FEFE。
GB2312编码范围:A1A1-FEFE,其中汉字的编码范围为B0A1-F7FE,第一字节0xB0-0xF7(对应区号:16-87),第二个字节0xA1-0xFE(对应位号:01-94)
注: 属于原标准码的部分仍然采取单字节编码
即汉字国标扩展码,
GBK编码,是对GB2312编码的扩展,因此完全兼容GB2312-80标准。GBK编码依然采用双字节编码方案,其编码范围:8140-FEFE,剔除xx7F码位,共23940个码位。共收录汉字和图形符号21886个,其中汉字(包括部首和构件)21003个,图形符号883个。GBK编码支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年12月15日正式发布,这一版的GBK规范为1.0版。
Windows 95系统就是以GBK为内码,又由于GBK同时也涵盖了Unicode所有CJK汉字,所以也可以和Unicode做一一对应
8140-FEFE (33088-65278)
全部编码分为三大部分:1.汉字区;2.图形符号区;3.用户自定义区(详情见特性码位分配及顺序)
几乎完美支持汉字,但是不支持部分国家的语言(如一些东亚国家,日本等)
所以经常会有GBK到UNICODE的转换
GBK 规范收录了 ISO 10646.1 中的全部 CJK 汉字和符号,并有所补充。具体包括:
1. GB 2312 中的全部汉字、非汉字符号。
2. GB 13000.1 中的其他 CJK 汉字。以上合计 20902 个 GB 化汉字。
3. 《简化字总表》中未收入 GB 13000.1 的 52 个汉字。
4. 《康熙字典》及《辞海》中未收入 GB 13000.1 的 28 个部首及重要构件。
5. 13 个汉字结构符。
6. BIG-5 中未被 GB 2312 收入、但存在于 GB 13000.1 中的 139 个图形符号。
7. GB 12345 增补的 6 个拼音符号。
8. 汉字“○”。
9. GB 12345 增补的 19 个竖排标点符号(GB12345 较 GB 2312 增补竖排标点符号 29 个,其中 10 个未被 GB 13000.1 收入,故 GBK 亦不收)。
10. 从 GB 13000.1 的 CJK 兼容区挑选出的 21 个汉字。
11. GB 13000.1 收入的 31 个 IBM OS/2 专用符号。
12.未录入《新华字典》上的一些字,如“韡”的简体。
GBK 亦采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。
全部编码分为三大部分:
1. 汉字区。包括:
a. GB 2312 汉字区。即 GBK/2: B0A1-F7FE。收录 GB 2312 汉字 6763 个,按原顺序排列。
b. GB 13000.1 扩充汉字区。包括:
(1) GBK/3: 8140-A0FE。收录 GB 13000.1 中的 CJK 汉字 6080 个。
(2) GBK/4: AA40-FEA0。收录 CJK 汉字和增补的汉字 8160 个。CJK 汉字在前,按 UCS 代码大小排列;增补的汉字(包括部首和构件)在后,按《康熙字典》的页码/字位排列。
2. 图形符号区。包括:
a. GB 2312 非汉字符号区。即 GBK/1: A1A1-A9FE。其中除 GB 2312 的符号外,还有 10 个小写罗马数字和 GB12345 增补的符号。计符号 717 个。
b. GB 13000.1 扩充非汉字区。即 GBK/5: A840-A9A0。BIG-5 非汉字符号、结构符和“○”排列在此区。计符号 166 个。
3. 用户自定义区:分为(1)(2)(3)三个小区。
(1) AAA1-AFFE,码位 564 个。
(2) F8A1-FEFE,码位 658 个。
(3) A140-A7A0,码位 672 个。
第(3)区尽管对用户开放,但限制使用,因为不排除未来在此区域增补新字符的可能性
GBK 对字形作了如下的规定:
1. 原则上与 GB 13000.1 G列(即源自中国大陆法定标准的汉字)下的字形/笔形保持一致。
2. 在 CJK 汉字认同规则的总框架内,对所有的 GBK 编码汉字实施“无重码正形”(“GB 化”);即在不造成重码的前提下,尽量采用中国新字形。
3. 对于超出 CJK 汉字认同规则的、或认同规则尚未明确规定的汉字,在 GBK 码位上暂安放旧字形。这样,在许多情况下 GBK 收入了同一汉字的新旧两种字形。
4. 非汉字符号的字形,凡 GB 2312 已经包括的,与 GB 2312 保持一致;超出 GB 2312 的部分,与 GB 13000.1 保持一致。
5. 带声调的拼音字母取半角形式。
低字节是0x40-0x7E的GBK字符有一定特殊性,因为这些字符占用了ASCII码的位置,这样会给一些系统带来麻烦。
有些系统中用0x40-0x7E中的字符(如“|”)做特殊符号,在定位这些符号时又没有判断这些符号是不是属于某个 GBK字符的低字节,这样就会造成错误判断。在支持GB2312的环境下就不存在这个问题。需要注意的是支持GBK的环境中小于0x80的某个字节未必就 是ASCII符号;另外就是最好选用小于0×40的ASCII符号做一些特殊符号,这样就可以快速定位,且不用担心是某个汉字的另一半。Big5编码中也存在相应问题。
遵循GB2312规定
2000年3月17日发布的汉字编码国家标准GB18030编码,是对GBK编码的扩充,覆盖中文、日文、朝鲜语和中国少数民族文字,其中收录27484个汉字。GB18030字符集采用单字节、双字节和四字节三种方式对字符编码。兼容GBK和GB2312字符集。
国家标准GB18030-2005《信息技术 中文编码字符集》是我国继GB2312-1980和GB13000-1993之后最重要的汉字编码标准,是我国计算机系统必须遵循的基础性标准之一。 GB18030有两个版本:GB18030-2000和GB18030-2005。GB18030-2000是GBK的取代版本,它的主要特点是在GBK基础上增加了CJK统一汉字扩充A的汉字。GB18030-2005的主要特点是在GB18030-2000基础上增加了CJK统一汉字扩充B的汉字。
GB18030-2000编码标准《信息技术汉字编码字符集 基本集的扩充》是由信息产业部和国家质量技术监督局在2000年3月17日联合发布的,并且将作为一项国家标准在2001年的1月正式强制执行。GB18030-2000仅规定了常用非汉字符号和27533个汉字(包括部首、部件等)的编码。
GB18030-2005《信息技术中文编码字符集》是以汉字为主并包含多种我国少数民族文字的超大型中文编码字符集,其中收入汉字70000余个。在GB18030-2000的基础上增加了42711个汉字和多种我国少数民族文字的编码(如藏、蒙古、傣、彝、朝鲜、维吾尔文等)。增加的这些内容是推荐性的,原GB18030-2000中的内容是强制性的,市场上销售的产品必须符合。故GB18030-2005为部分强制性标准,自发布之日起代替GB18030-2000。
采用单字节、双字节、四字节分段编码方案,具体码位见特性。
GB18030向下兼容GBK和GB2312编码。
GB18030可用于一切处理中文(包括汉字和少数民族文)信息,特别是汉字信息的信息处理产品。
GB18030-2000标准收录的字符分别以单字节、双字节和四字节编码。
1、单字节部分
本标准中,单字节的部分收录了GB11383的0x00到0x7F全部128个字符及单字节编码的欧元符号。
2、双字节部分
本标准中,双字节的部分收录内容如下:
GB 13000.1的全部CJK统一汉字字符。
GB 13000.1的CJK兼容区挑选出来的21个汉字。
GB 13000.1中收录而GB 2312未收录的我国台湾地区使用的图形字符139个。
GB 13000.1收录的其它字符31个。
GB 2312中的非汉字符号。
GB 12345 的竖排标点符号19个。
GB 2312未收录的10个小写罗马数字。
GB 2312未收录的带音调的汉语拼音字母5个以及ɑ 和ɡ。
汉字数字“〇”。
表意文字描述符13个。
增补汉字和部首/构件80个。
双字节编码的欧元符号。
3 、四字节部分
本标准的四字节的部分,收录了上述双字节字符之外的,包括CJK统一汉字扩充A在内的GB 13000.1 中的全部字符。
GB18030-2005标准收录的字符分别以单字节、双字节或四字节编码。
1、单字节部分
本标准中,单字节的部分收录了GB/T11383-1989的0x00到0x7F全部128个字符。
2、双字节部分
本标准中,双字节的部分收录内容如下:
GB 13000.1-1993的全部CJK统一汉字字符。
GB 13000.1-1993的CJK兼容区挑选出来的21个汉字。
GB 13000.1-1993中收录而GB 2312未收录的我国台湾地区使用的图形字符139个。
GB 13000.1-1993收录的其它字符31个。
GB 2312中的非汉字符号。
GB 12345 的竖排标点符号19个。
GB 2312未收录的10个小写罗马数字。
GB 2312未收录的带音调的汉语拼音字母5个以及ɑ 和ɡ。
汉字数字“〇”。
表意文字描述符13个。
对GB 13000.1-1993增补的汉字和部首/构件80个。
双字节编码的欧元符号。
3、四字节部分
本标准的四字节的部分,收录了上述双字节字符之外的,GB 13000的CJK统一汉字扩充A、CJK统一汉字扩充B和已经在GB13000中编码的我国少数民族文字的字符。
GB18030-2005最主要的变化是增加了CJK统一汉字扩充B。它还去掉了单字节编码的欧元符号0x80)。
GB18030有1611668个码位,在GB18030-2005中定义了76556个字符。随着我国汉字整理和编码研究工作的不断深入,以及国际标准ISO/IEC 10646的不断发展,GB18030所收录的字符将在新版本中增加。
如下表所示,GB18030-2000收录了27533个汉字:
类别 |
码位范围 |
码位数 |
字符数 |
字符类型 |
双字节部分 |
第一字节0xB0-0xF7 |
6768 |
6763 |
汉字 |
第二字节0xA1-0xFE |
||||
第一字节0x81-0xA0 |
6080 |
6080 |
汉字 |
|
第二字节0x40-0xFE |
||||
第一字节0xAA-0xFE |
8160 |
8160 |
汉字 |
|
第二字节0x40-0xA0 |
||||
四字节部分 |
第一字节0x81-0x82 |
6530 |
6530 |
CJK统一汉字扩充A |
第二字节0x30-0x39 |
||||
第三字节0x81-0xFE |
||||
第四字节0x30-0x39 |
27533就是6763+6080+8160+6530。双字节部分的6763+6080+8160=21003个汉字就是GBK的21003个汉字。
在Unicode中,CJK统一汉字扩充A有6582个汉字,为什么这里只有6530个汉字?
这是因为在GBK时代,双字节部分已经收录过CJK统一汉字扩充A的52个汉字,所以还余6530个汉字。
如下表所示,GB18030-2005收录了70244个汉字:
类别 |
码位范围 |
码位数 |
字符数 |
字符类型 |
双字节部分 |
第一字节0xB0-0xF7 |
6768 |
6763 |
汉字 |
第二字节0xA1-0xFE |
||||
第一字节0x81-0xA0 |
6080 |
6080 |
汉字 |
|
第二字节0x40-0xFE |
||||
第一字节0xAA-0xFE |
8160 |
8160 |
汉字 |
|
第二字节0x40-0xA0 |
||||
四字节部分 |
第一字节0x81-0x82 |
6530 |
6530 |
CJK统一汉字扩充A |
第二字节0x30-0x39 |
||||
第三字节0x81-0xFE |
||||
第四字节0x30-0x39 |
||||
第一字节0x95-0x98 |
42711 |
42711 |
CJK统一汉字扩充B |
|
第二字节0x30-0x39 |
||||
第三字节0x81-0xFE |
||||
第四字节0x30-0x39 |
BIG5编码又称大五码,是繁体中文字符集编码标准,共收录13060个中文字,其中有二字为重复编码。
BIG5采用双字节编码,使用两个字节来表示一个字符。高位字节使用了0x81-0xFE,低位字节使用了0x40-0x7E,及0xA1-0xFE。在BIG5的分区中:
8140-A0FE 保留给使用者自定义字符(造字区)
A140-A3BF 标点符号、希腊字母及特殊符号。其中在A259-A261,收录了度量衡单位用字:兙兛兞兝兡兣嗧瓩糎。
A3C0-A3FE 保留。此区没有开放作造字区用。
A440-C67E 常用汉字,先按笔划再按部首排序。
C6A1-F9DC 其它汉字。
F9DD-F9FE 制表符。
值得留意的是,BIG5重复地收录了两个相同的字:“兀、兀”(A461及C94A)、“嗀、嗀”(DCD1及DDFC)。
8140-FEFE(33088-65278)
其中汉字编码范围:A440-F9DC(42048-63964)
适用于台湾和香港地区的繁体中文系统软件等.不过由于编码本身存在的问题.已经基本改用Unicode编码了.
由于各厂商及政府推出的Big5延伸,彼此互不兼容,造成乱码问题。
因为低位元字符中包含了编程语言、shell、script 中,字串或命令常会用到的特殊字符,例如0x5C “\”、0x7C “|”等。“\”在许多用途的字串中是当作转义符号又称为跳脱字符,例如 \n(换行)、\r(归位)、\t(tab)、\\(\本身符号)、\"(引号)等等。而 “|” 在UNIX操作系统中大多当作命令管线的使用,如 "ls -la | more" 等等。如果在字串中有这些特殊的转义字符,会
BIG-5
BIG-5
被程式或直译器解释为特殊用途。但是因为是中文的原因,故无法正确解释为上面所述的行为,因此程式可能会忽略此转义符号或是中断执行。若此,就违反了使用者本来要当成中文字符一部份使用的本意。
在常用字如“功”(0xA55C)、“许”(0xB35C)、“盖”(0xBB5C)、“育”(0xA87C)中时常出现,造成了许多软件无法正确处理以Big5编码的字串或文件。这个问题被戏谑性地人名化,称为“许功盖”或“许盖功”(这三个字都有这种问题)。但是额外的困扰是,有些输出功能并不会把“\”当作特殊字符看待,所以有些程式或网页就会错误地常常出现在“许功盖”这些字后面多了“\”。
在倚天中文系统,以及后来的Windows3.1、95及98中,定义了四个私人造字区范围:0xFA40-0xFEFE、0x8E40-0xA0FE、0x8140-0x8DFE、0xC6A1-0xC8FE。私人造字区的原意,是供使用者加入本来在编码表中缺少的字符,但当每个使用者都在不同的地方加上不同的字符后,当交换资料时,对方便难以知道某一个编码究竟想表达什么字
Big5是双字节编码,高字节编码范围是0x81-0xFE,低字节编码范围是0x40-0x7E和0xA1-0xFE。和GBK相比,少了低字节是0x80-0xA0的组合。0x8140-0xA0FE是保留区域,用于用户造字区
Big5收录的汉字只包括繁体汉字,不包括简体汉字,一些生僻的汉字也没有收录。GBK收录的日文假名字符、俄文字符Big5也没有收录。因为Big5当中收录的字符有限,因此有很多在Big5基础上扩展的编码,如倚天中文系统。Windows系统上使用的代码页CP950也可以理解为是对Big5的扩展,在Big5的基础上增加了7个汉字和一些符号。Big5编码对应的字符集是GBK字符集的子集,也就是说Big5收录的字符是GBK收录字符的一部分,但相同字符的编码不同。
会有
因为Big5也占用了ASCII的编码空间(低字节所使用的0x40-0x7E),所以Big5编码在一些环境下存在和GBK编码相同的问题,即低字节范围为0x40-0x7E的字符有可能会被误处理,尤其是低字节是0x5C("/")和0x7C("|")的字符。可以参考GBK一节相应说明。
Unicode 是全球文字统一编码。它把世界上的各种文字的每一个字符指定唯一编码,实现跨语种、跨平台的应用。
Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。
Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。
根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载:历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。
在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。
目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode4.1.0。ISO的最新标准是ISO 10646-3:2003。
UCS只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个编码;也可以用utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。UTF是“UCS Transformation Format”的缩写。
IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。
详情百度Unicode编码范围.
例如,中文范围为: 4E00-9FBF,其中
CJK 统一表意符号(4DC0-4DFF)等等…
Unicode是一种规范,具体的实现由各自实现的编码方式决定.
作为一种全球统一编码,适用于世界范围,有多种实现方式.
(如utf-8,utf-16,utf-32)
Unicode只是一种规范,没有明确规定实现方式.所有不同的实现方案有着不同的存储方式等
(如utf-8,utf-16,utf-32的区别)
UTF-8是Unicode的一种实现方式,根据不同的Unicode字符,用1到6个字节编码.
UTF-8的字节结构有特殊要求,比如我们说一个汉字的范围是0X4E00到0x9FA5,是指unicode值,至于放在utf-8的编码里去就是由三个字节来组织.
注:遵循Unicode规范.
根据不同的Unicode字符,用1到6个字节编码
UTF8是采用变长的编码方式,为1~6个字节,但通常我们只把它看作单字节或三字节的实现,因为其它情况实在少见。UTF8编码通过多个字节组合的方式来显示,这是计算机处理UTF8的机制,它是无字节序之分的,并且每个字节都非常有规律,详见上图
UTF-16是Unicode的一种实现方式.
UTF-16是完全对应于UCS-2的,即把UCS-2规定的代码点通过Big Endian或Little Endian方式直接保存下来。所以UTF-16采用2个字节来存储Unicode。UTF-16也可以表示UCS-4的部分字符,所以UTF-16也采用4个字节来存储Unicode。
UTF16编码是Unicode最直接的实现方式,通常我们在windows上新建文本文件后保存为Unicode编码,其实就是保存为UTF16编码。
1. Javascript内置的实现就是UTF-16编码
2. UTF-16比起UTF-8,好处在于大部分字符都以固定长度的字节 (2字节) 储存,但UTF-16却无法兼容于ASCII编码。
3. 大端字节序,高字节存于内存低地址,低字节存于内存高地址;小端字节序反之
如一个long型数据0x12345678
大端字节序:
内存低地址--> 0x12
0x34
0x56
内存高地址--> 0x78
小端字节序:
内存低地址--> 0x78
0x56
0x34
内存高地址--> 0x12
由于UTF-16固定是两字节的(UCS-4部分字符4字节),所以无法支持单字节的ASCII码
因为是多字节存储,所以它的存储方式分为2种:大端序和小端序。
UTF16编码在windows上采用小端序的方式存储;而比如JavaScript语言,它内部就是采用UTF16编码,并且它的存储方式为大端序。
UTF-32是Unicode的一种实现方式.
UTF32编码使用固定的4个字节来存储。
因此,非常浪费空间,不利于网络传输,所以使用不普遍。
Tips: Html5中明确规定禁止使用Utf-32编码.
浪费空间,使用不普遍
(American National Standards Institute) 美国国家标准学会的缩写
针对汉字的编码,不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、Big5、Shift_JIS 等各自的编码标准。这些使用 1 至 4 个字节来代表一个字符的各种汉字延伸编码方式,称为ANSI 编码。
例如:在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文操作系统中,ANSI编码代表Big5编码;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。UTF16和UTF8之间的相互转换可以通过上图的转换表来实现,判断Unicode码所在的区间就可以得到这个字符是由几个字节所组成,之后通过移位来实现,分为新的多个字节来存储。
Step1:获取该字符对应的Unicode码
Step2:判断该Unicode码所在的范围,根据不同的范围,来决定存储它的字节长度.
*如果介于U+00000000 – U+0000007F之间,代表该字符采取一个字节存储,那么直接通过这个新字节的unicode码,即可转换为UTF-8码(这是这里的一种简称,不同的编程语言有不同实现,例如可以用两个字节来存储一个字符的信息,解码时进行判断,如果发现是UTF-8的多字节实现,那么将多字节合并后再转为一个字符输出).转换完毕
*如果介于U+00000080 – U+000007FF之间,代表该字符采取两个字节存储,那么将该Unicode码转为二进制,取出高5位(这里不分大端序和小端序,只以实际的码为准,具体实现可以采取移位实现),并加上头部110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节。然后分别通过两个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
*如果介于U+00000800 – U+0000FFFF之间,代表该字符采取三个字节存储,那么将该Unicode码转为二进制,取出高4位,并加上头部1110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节。然后分别通过三个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
*如果介于U+00010000 – U+001FFFFF之间,代表该字符采取四个字节存储(实际上,四个字节或以上存储的字符是很少的),那么将该Unicode码转为二进制,取出高3位,并加上头部11110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节;再取出低6位(按顺序取),加上头部10,组成第四个字节。然后分别通过四个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
*如果介于U+00200000 – U+03FFFFFF,代表该字符采取五个字节存储,那么将该Unicode码转为二进制,取出高2位,并加上头部111110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节;再取出低6位(按顺序取),加上头部10,组成第四个字节;再取出低6位(按顺序取),加上头部10,组成第五个字节。然后分别通过五个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
*如果介于U+04000000 – U+7FFFFFFF,代表该字符采取六个字节存储,那么将该Unicode码转为二进制,取出高1位,并加上头部1111110,组成第一个字节;再取出低6位(按顺序取),加上头部10,组成第二个字节;再取出低6位(按顺序取),加上头部10,组成第三个字节;再取出低6位(按顺序取),加上头部10,组成第四个字节;再取出低6位(按顺序取),加上头部10,组成第五个字节;再取出低6位(按顺序取),加上头部10,组成第六个字节。然后分别通过六个新的字节的unicode码,可以转换为相应的UTF-8码.转换完毕
见示例源码(以JS为例子)
/**
* @description 将utf-16编码字符串转为utf-8编码字符串
* @param {String} str 传入的 utf16编码字符串(javascript内置的就是utf16编码)
* @return {String} utf8编码的字符串,js打印会有乱码
*/
exports.UTF16StrToUTF8Str = function(str) {
if (!str) {
//''字符属于ascii码,所以不必担心不同编码的转换问题
return '';
}
//res是用来存放结果的字符数组,最终会转为字符串返回
var res = [],
len = str.length;
for (var i = 0; i < len; i++) {
var code = str.charCodeAt(i);
if (code > 0x0000 && code <= 0x007F) {
// 单字节,这里并不考虑0x0000,因为它是空字节
// U+00000000 – U+0000007F 0xxxxxxx
res.push(str.charAt(i));
} else if (code >= 0x0080 && code <= 0x07FF) {
// 双字节
// U+00000080 – U+000007FF 110xxxxx 10xxxxxx
// 110xxxxx
//0xC0 为12*16 = 192 二进制为 11000000
//0x1F为 31 二进制 00011111,因为第一个字节只取5位
//code 右移六位是因为从高位开始取得,所以需要将低位的六位留到第二个字节
var byte1 = 0xC0 | ((code >> 6) & 0x1F);
// 10xxxxxx
//0x80为128 二进制为 10000000
//0x3F为63 二进制位 00111111,因为只需要取到低位的6位
var byte2 = 0x80 | (code & 0x3F);
res.push(
String.fromCharCode(byte1),
String.fromCharCode(byte2)
);
} else if (code >= 0x0800 && code <= 0xFFFF) {
// 三字节
// U+00000800 – U+0000FFFF 1110xxxx 10xxxxxx 10xxxxxx
// 1110xxxx
//0xE0 为224 二进制为 11100000
//同理,需要留下 12位给低位
//0x0F为15 00001111
var byte1 = 0xE0 | ((code >> 12) & 0x0F);
// 10xxxxxx
//再留6位给低位
var byte2 = 0x80 | ((code >> 6) & 0x3F);
// 10xxxxxx
var byte3 = 0x80 | (code & 0x3F);
res.push(
String.fromCharCode(byte1),
String.fromCharCode(byte2),
String.fromCharCode(byte3)
);
} else if (code >= 0x00010000 && code <= 0x001FFFFF) {
// 四字节
// U+00010000 – U+001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
//同理,需要留下 18位给低位
//0x07 00000111
//0xF0 240 11110000
var byte1 = 0xF0 | ((code >> 18) & 0x07);
// 10xxxxxx
//再留12位给低位
var byte2 = 0x80 | ((code >> 12) & 0x3F);
//再留6位给低位
var byte3 = 0x80 | ((code >> 6) & 0x3F);
// 10xxxxxx
var byte4 = 0x80 | (code & 0x3F);
res.push(
String.fromCharCode(byte1),
String.fromCharCode(byte2),
String.fromCharCode(byte3),
String.fromCharCode(byte4)
);
} else if (code >= 0x00200000 && code <= 0x03FFFFFF) {
// 五字节
// U+00200000 – U+03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
//同理,需要留下 24位给低位
//0x03 00000011
//0xF8 248 11111000
var byte1 = 0xF8 | ((code >> 24) & 0x03);
// 10xxxxxx
//再留18位给低位
var byte2 = 0x80 | ((code >> 18) & 0x3F);
//再留12位给低位
var byte3 = 0x80 | ((code >> 12) & 0x3F);
//再留6位给低位
var byte4 = 0x80 | ((code >> 6) & 0x3F);
// 10xxxxxx
var byte5 = 0x80 | (code & 0x3F);
res.push(
String.fromCharCode(byte1),
String.fromCharCode(byte2),
String.fromCharCode(byte3),
String.fromCharCode(byte4),
String.fromCharCode(byte5)
);
} else /** if (code >= 0x04000000 && code <= 0x7FFFFFFF)*/ {
// 六字节
// U+04000000 – U+7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
//同理,需要留下 24位给低位
//0x01 00000001
//0xFC 252 11111100
var byte1 = 0xFC | ((code >> 30) & 0x01);
// 10xxxxxx
//再留24位给低位
var byte2 = 0x80 | ((code >> 24) & 0x3F);
//再留18位给低位
var byte3 = 0x80 | ((code >> 18) & 0x3F);
//再留12位给低位
var byte4 = 0x80 | ((code >> 12) & 0x3F);
//再留6位给低位
var byte5 = 0x80 | ((code >> 6) & 0x3F);
// 10xxxxxx
var byte6 = 0x80 | (code & 0x3F);
res.push(
String.fromCharCode(byte1),
String.fromCharCode(byte2),
String.fromCharCode(byte3),
String.fromCharCode(byte4),
String.fromCharCode(byte5),
String.fromCharCode(byte6)
);
}
}
return res.join('');
};
这是UTF8转换到UTF16,同样需要对照转换表来实现。需要判断字符的Unicode码,然后判断是属于用几个字节存储的,然后分别用对应的字节数合并起来,形成新的字符输出.
Step1:获取该字符对应的Unicode码
Step2:用该码的二进制和相应的关键字节相与,根据上图,判断处于那一段区间,来判断是使用几个字节存储字符的,然后分别合并对应的字节数,组成新的字符输出。
*用该Unicode码的二进制右移7位后与(11111111)相与,如果得到了0,代表该字符只用了一个字节存储,所以直接输出该字符.转换完毕
*用该Unicode码的二进制右移5位后与(11111111)相与,如果得到了110(6),代表该字符占用了二个字节,所以分别获取该字符和下一个字符,然后分别取出本字节的低5位后左移6位和取出下一个字节的低6位(保持不变),将2个字节相或,得到一个新的字节.这个字节就是最终字符的unicode码,然后转为对应的字符输出. 转换完毕
*用该Unicode码的二进制右移4位后与(11111111)相与,如果得到了1110(14),代表该字符占用了三个字节,所以分别获取该字符和下一个字符和下下个字符,然后分别取出本字节的低4位后左移12位和取出下一个字节的低6位后左移6位和取出下下一个字节的低6位(保持不变),将3个字节相或,得到一个新的字节.这个字节就是最终字符的unicode码,然后转为对应的字符输出. 转换完毕
*用该Unicode码的二进制右移3位后与(11111111)相与,如果得到了11110(30),代表该字符占用了四个字节,所以分别获取该字符和下一个字符和下下个字符和下下下个字符,然后分别取出本字节的低3位后左移18位取出下一个字节的低6位后左移12位和和取出下下一个字节的低6位后左移6位和取出下下下一个字节的低6位(保持不变),将4个字节相或,得到一个新的字节.这个字节就是最终字符的unicode码,然后转为对应的字符输出. 转换完毕
*用该Unicode码的二进制右移2位后与(11111111)相与,如果得到了111110(62),代表该字符占用了五个字节,所以分别获取该字符和下一个字符和下下个字符和下下下个字符和下下下下个字符,然后分别取出本字节的低2位后左移24位和取出下一个字节的低6位后左移18位和取出下下一个字节的低6位后左移12位和和取出下下下一个字节的低6位后左移6位和取出下下下下一个字节的低6位(保持不变),将5个字节相或,得到一个新的字节.这个字节就是最终字符的unicode码,然后转为对应的字符输出. 转换完毕
*用该Unicode码的二进制右移1位后与(11111111)相与,如果得到了1111110(126),代表该字符占用了六个字节,所以分别获取该字符和下一个字符和下下个字符和下下下个字符和下下下下个字符和下下下下下个字符,然后分别取出本字节的低1位后左移30位和取出下一个字节的低6位后左移24位和取出下下一个字节的低6位后左移18位和取出下下下一个字节的低6位后左移12位和和取出下下下下一个字节的低6位后左移6位和取出下下下下下一个字节的低6位(保持不变),将6个字节相或,得到一个新的字节.这个字节就是最终字符的unicode码,然后转为对应的字符输出. 转换完毕
见示例源码(以JS为例子)
/**
* @description UTF8编码字符串转为UTF16编码字符串
* @param {String} str utf8编码的字符串
* @return {String} utf16编码的字符串,可以直接被js用来打印
*/
exports.UTF8StrToUTF16Str = function(str) {
if (!str) {
return '';
}
//res是用来存放结果的字符数组,最终会转为字符串返回
var res = [],
len = str.length;
for (var i = 0; i < len; i++) {
//获得对应的unicode码
var code = str.charCodeAt(i);
// 对第一个字节进行判断
if (((code >> 7) & 0xFF) == 0x0) {
//0xFF 255 11111111,代表只取前8位
//右移7位,如果是只剩下0了,代表这个是单字节
// 单字节
// 0xxxxxxx
res.push(str.charAt(i));
} else if (((code >> 5) & 0xFF) == 0x6) {
// 双字节 110开头
// 110xxxxx 10xxxxxx
//需要用到下一个字节
var code2 = str.charCodeAt(++i);
//0x1F 31 00011111
//取到第一个字节的后5位,然后左移6位(这6位留给第二个字节的低6位),由于js是number型,所以不必担心溢出
var byte1 = (code & 0x1F) << 6;
//0x3F 63 00111111
var byte2 = code2 & 0x3F;
//或运算,因为第一个字节第六位没有,第二个字节只有低6位,所以算是结合了
var utf16 = byte1 | byte2;
res.push(String.fromCharCode(utf16));
} else if (((code >> 4) & 0xFF) == 0xE) {
// 三字节 1110开头
// 1110xxxx 10xxxxxx 10xxxxxx
var code2 = str.charCodeAt(++i);
var code3 = str.charCodeAt(++i);
//和00001111与后, 左移12位
var byte1 = (code & 0x0F) << 12;
//和00111111与后,左移6位
var byte2 = (code2 & 0x3F) << 6;
//和00111111与
var byte3 = code3 & 0x3F
var utf16 = byte1 | byte2 | byte3;
res.push(String.fromCharCode(utf16));
} else if (((code >> 3) & 0xFF) == 0x1E) {
// 四字节 11110开头
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
var code2 = str.charCodeAt(++i);
var code3 = str.charCodeAt(++i);
var code4 = str.charCodeAt(++i);
//和00000111与后, 左移18位
var byte1 = (code & 0x07) << 18;
//和00111111与后,左移12位
var byte2 = (code2 & 0x3F) << 12;
//和00111111与后,左移6位
var byte3 = (code3 & 0x3F) << 6;
//和00111111与
var byte4 = code4 & 0x3F
var utf16 = byte1 | byte2 | byte3 | byte4;
res.push(String.fromCharCode(utf16));
} else if (((code >> 2) & 0xFF) == 0x3E) {
// 五字节 111110开头
// 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
var code2 = str.charCodeAt(++i);
var code3 = str.charCodeAt(++i);
var code4 = str.charCodeAt(++i);
var code5 = str.charCodeAt(++i);
//和00000011与后, 左移24位
var byte1 = (code & 0x03) << 24;
//和00111111与后,左移18位
var byte2 = (code2 & 0x3F) << 18;
//和00111111与后,左移12位
var byte3 = (code3 & 0x3F) << 12;
//和00111111与后,左移6位
var byte4 = (code4 & 0x3F) << 6;
//和00111111与
var byte5 = code5 & 0x3F
var utf16 = byte1 | byte2 | byte3 | byte4 | byte5;
res.push(String.fromCharCode(utf16));
} else /** if (((code >> 1) & 0xFF) == 0x7E)*/ {
// 六字节 1111110开头
// 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
var code2 = str.charCodeAt(++i);
var code3 = str.charCodeAt(++i);
var code4 = str.charCodeAt(++i);
var code5 = str.charCodeAt(++i);
var code6 = str.charCodeAt(++i);
//和00000001与后, 左移30位
var byte1 = (code & 0x01) << 30;
//和00111111与后,左移24位
var byte2 = (code2 & 0x3F) << 24;
//和00111111与后,左移18位
var byte3 = (code3 & 0x3F) << 18;
//和00111111与后,左移12位
var byte4 = (code4 & 0x3F) << 12;
//和00111111与后,左移6位
var byte5 = (code5 & 0x3F) << 6;
//和00111111与
var byte6 = code6 & 0x3F
var utf16 = byte1 | byte2 | byte3 | byte4 | byte5 | byte6;
res.push(String.fromCharCode(utf16));
}
}
return res.join('');
};
这里以UTF-16和GBK之间的转换为例(示例用的Javascript语言,内置UTF-16编码)
UTF-16转为GBK,需要先得知两者之间的映射表才行.(可以百度搜索Unicode和GBK之间的转换表),然后根据两者的映射关系,进行一一对应转换.
Step1:获取该字符对应的Unicode码
Step2:判断该Unicode的范围,如果是普通的ASCII码,则不进行转换,如果是大于127小于等于255的(标准码范围的,GBK兼容ISO-8859标准码的),根据映射表,转为对应的GBK码,如果是大于255的(大于255代表一个字节装不下了,所以这时候不再是兼容模式,而是GBK的存储模式,需要两个字节来存储),就将改码转换为GBK独特的双字节存储方式来存储(高字节区存储分区号,低字节去存储码号).转换完毕
见示例源码(js为例)
/**
* @description 将utf16编码的字符串(js内置编码)转为GBK编码的字符串
* @param {String} str utf16编码的字符串(js内置)
* @return {String} 转换后gbk编码的字符串
*/
exports.UTF16StrToGBKStr = function(str) {
if (!str) {
return '';
}
//res是用来存放结果的字符数组,最终会转为字符串返回
var res = [],
len = str.length;
for (var i = 0; i < len; i++) {
//获得对应的unicode码
var code = str.charCodeAt(i);
if (code < 0) {
code += 65536;
}
if (code > 127) {
code = UniCode2GBKCode(code);
}
if (code > 255) {
//gbk中,如果是汉字的,需要两位来表示
//对所收录字符进行了“分区”处理,分为若干区,每区若干码位
//第一个字节为“高字节”,对应不同分区
//第二个字节为“低字节”,对应每个区的不同码位
var varlow = code & 65280;
//取得低位
varlow = varlow >> 8;
//取得高位
var varhigh = code & 255;
res.push(String.fromCharCode(varlow));
res.push(String.fromCharCode(varhigh));
} else {
res.push(String.fromCharCode(code));
}
}
return res.join('');
};
/**
* @description 将unicode通过查表转换,转为gbk的code
* @param {Number} chrCode 字符unicode编码
*/
function UniCode2GBKCode(chrCode) {
//这里UnicodeCharTable和GBKCharTable由于太长了,就没有给出了,可以下载源码例子查看
var chrHex = chrCode.toString(16);
chrHex = "000" + chrHex.toUpperCase();
chrHex = chrHex.substr(chrHex.length - 4);
var i = UnicodeCharTable.indexOf(chrHex);
if (i != -1) {
chrHex = GBKCharTable.substr(i, 4);
}
return parseInt(chrHex, 16)
};
GBK转为UTF-16,也需要根据Unicode和GBK之间的映射表,然后根据两者的映射关系,进行一一对应转换.
Step1:获取该字符对应的Unicode码
Step2:判断该Unicode的范围,如果是普通的ASCII码,则不进行转换,直接输出,否则,需要根据GBK和Unicode的对应关系,转换为Unicode码,需要注意的是,这里由于GBK采取双字节编码的,所以需要用到两个字节,转码时需要将编码时的运算逆转,转为Unicode码,然后再输出相应的字符.转换完毕
三、 见示例源码(js)
/**
* @description将 gbk的对应的code通过查表转换,转为unicode
* @param {Number} chrCode gbk字符对应的编码
*/
function GBKCode2UniCode(chrCode) {
//这里UnicodeCharTable和GBKCharTable由于太长了,就没有给出了,可以下载源码例子查看
//以16进制形式输出字符串
var chrHex = chrCode.toString(16);
//
chrHex = "000" + chrHex.toUpperCase();
//
chrHex = chrHex.substr(chrHex.length - 4);
var i = GBKCharTable.indexOf(chrHex);
if (i != -1) {
chrHex = UnicodeCharTable.substr(i, 4);
}
return parseInt(chrHex, 16)
};
/**
* @description 将GBK编码的字符串转为utf16编码的字符串(js内置编码)
* @param {String} str GBK编码的字符串
* @return {String} 转化后的utf16字符串
*/
exports.GBKStrToUTF16Str = function(str) {
if (!str) {
return '';
}
//res是用来存放结果的字符数组,最终会转为字符串返回
var res = [],
len = str.length;
for (var i = 0; i < len; i++) {
//获得对应的unicode码
var code = str.charCodeAt(i);
//如果不是ASCII码
if (code > 127) {
//转为unicode
//这里左移8位是因为编码时,被右移了8位
code = GBKCode2UniCode((code << 8) + str.charCodeAt(++i));
} else {
//普通的ASCII码,什么都不做
}
res.push(String.fromCharCode(code));
}
return res.join('');
};
Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。由于2的6次方为64,所以每6个位为一个单元,对应某个可打印字符。当原数据不是3的整数倍时,如果最后剩下两个输入数据,在编码结果后加1个“=;如果最后剩下一个输入数据,编码结果后加2个“=;如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。
解码是编码的逆过程,先看后面补了几个“=”号,最多只可能补2个“=”号。一个“=”相当于补了2个0,所以去掉后面补的0后,再按8位展开,即可还原。
以”Word”字符串的编码和解码为例。
原始字符 |
W |
o |
r |
d(由于不是3的倍数,所以补0了) |
||||||||
ASCII码 |
87 |
111 |
114 |
100 |
||||||||
8bit字节 |
01010111 |
01101111 |
01110010 |
01100100 |
00000000 |
00000000 |
||||||
6bit字节 |
010101 |
110110 |
111101 |
110010 |
011001 |
000000 |
000000 |
000000 |
||||
B64十进制 |
21 |
54 |
61 |
50 |
25 |
0 (注意,这里有两位是d里面的,所以是正常的0) |
异常 (注意这里不能按正规的0来算) |
异常 (需要补上=号) |
||||
对应编码 |
V |
2 |
9 |
y |
Z |
A |
= |
= |
所以’Word’的编码结果是V29yZA==
原始编码 |
V |
2 |
9 |
y |
Z |
A |
= |
= |
||||
B64十进制 |
21 |
54 |
61 |
50 |
25 |
0 |
异常 |
异常 |
||||
6bit字节 |
010101 |
110110 |
111101 |
110010 |
011001 |
000000 |
000000 |
000000 |
||||
8bit字节 |
01010111 |
01101111 |
01110010 |
01100100 |
00000000 |
00000000 |
||||||
ASCII码 |
87 |
111 |
114 |
100 |
异常 |
异常 |
||||||
对应字符 |
W |
o |
r |
d |
无 |
无 |
由此可见,解码过程就是编码过程的逆过程。
需要注意的是,实际编码时需要注意程序内部的编码,例如Javascript内置的是UTF-16编码,所以如果编码成GBK或UTF-8时需要经过一定的转换.
详情见示例源码
/**
* @description 创建一个base64对象
*/
(function(base64) {
/**
* Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),
* 之后在6位的前面补两个0,形成8位一个字节的形式。
* 由于2的6次方为64, 所以每6个位为一个单元, 对应某个可打印字符。
* 当原数据不是3的整数倍时, 如果最后剩下两个输入数据,
* 在编码结果后加1个“=;如果最后剩下一个输入数据,编码结果后加2个“=;
* 如果没有剩下任何数据,就什么都不要加,这样才可以保证资料还原的正确性。
*/
/**
* base64转码表,最后一个=号是专门用来补齐的
*/
var keyTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
/**
* @description 将一个目标字符串编码为base64字符串
* @param {String} str 传入的目标字符串
* 可以是任何编码类型,传入什么类型就输出成了什么样的编码
* 由于js内置是utf16编码,而服务器端一般不使用这种,
* 所以传入的编码一般是采取utf8或gbk的编码
* @return {String} 编码后的base64字符串
*/
function encodeBase64(str) {
if (!str) {
return '';
}
var i = 0; // 遍历索引
var len = str.length;
var res = [];
var c1, c2, c3 = '';
//用来存对应的位置
var enc1, enc2, enc3, enc4 = '';
while (i < len) {
c1 = str.charCodeAt(i++) & 0xFF;
c2 = str.charCodeAt(i++);
c3 = str.charCodeAt(i++);
enc1 = c1 >> 2;
enc2 = ((c1 & 0x3) << 4) | ((c2 >> 4) & 0x0F);
enc3 = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6);
enc4 = c3 & 0x3F;
//专门用来补齐=号的
if (isNaN(c2)) {
enc3 = enc4 = 0x40;
} else if (isNaN(c3)) {
enc4 = 0x40;
}
res.push(keyTable.charAt(enc1));
res.push(keyTable.charAt(enc2));
res.push(keyTable.charAt(enc3));
res.push(keyTable.charAt(enc4));
c1 = c2 = c3 = "";
enc1 = enc2 = enc3 = enc4 = "";
}
return res.join('');
};
/**
* @description 解码base64字符串,还原为编码前的结果
* @param {String} str 传入的目标字符串
* 可以是任何编码类型,传入什么类型就输出成了什么样的编码
* 由于js内置是utf16编码,而服务器端一般不使用这种,
* 所以传入的编码一般是采取utf8或gbk的编码
* @return {String} 编码后的base64字符串
*/
function decodeBase64(str) {
if (!str) {
return '';
}
//这里要判断目标字符串是不是base64型,如果不是,直接就不解码了
//两层判断
if (str.length % 4 != 0) {
return "";
}
var base64test = /[^A-Za-z0-9\+\/\=]/g;
if (base64test.exec(str)) {
return "";
}
var len = str.length;
var i = 0;
var res = [];
var code1, code2, code3, code4;
while (i < len) {
code1 = keyTable.indexOf(str.charAt(i++));
code2 = keyTable.indexOf(str.charAt(i++));
code3 = keyTable.indexOf(str.charAt(i++));
code4 = keyTable.indexOf(str.charAt(i++));
c1 = (code1 << 2) | (code2 >> 4);
c2 = ((code2 & 0xF) << 4) | (code3 >> 2);
c3 = ((code3 & 0x3) << 6) | code4;
res.push(String.fromCharCode(c1));
if (code3 != 64) {
res.push(String.fromCharCode(c2));
}
if (code4 != 64) {
res.push(String.fromCharCode(c3));
}
}
return res.join('');
};
/**
* @description 将utf16字符串转为utf8编码类型的base64编码
* 注意:js内置的编码是utf16型,所以传入的是utf16型码
* @param {String} str 传入的utf16编码
* @return {String} 编码后的utf8型base64字符串
*/
base64.encode_Utf8 = function(str) {
// 转成UTF8
var utf8 = exports.UTF16StrToUTF8Str(str);
return encodeBase64(utf8);
};
/**
* @description utf8编码的base64解密,解密成为js内置的utf16编码
* 注意:这里会讲传入的字符串当成utf8型,如果类型不匹配,就会产生错误结果
* @param {String} str 传入的utf8编码型base64字符串
* @return {String} 解码后的utf16字符串
*/
base64.decode_Utf8 = function(str) {
var decodeStr = decodeBase64(str);
// 转成UTF16
return exports.UTF8StrToUTF16Str(decodeStr);
};
/**
* @description 将utf16字符串转为GBK编码类型的base64编码
* 注意:js内置的编码是utf16型,所以传入的是utf16型码
* @param {String} str 传入的utf16编码
* @return {String} 编码后的gbk型base64字符串
*/
base64.encode_GBK = function(str) {
// 转成GBK
var GBK = exports.UTF16StrToGBKStr(str);
return encodeBase64(GBK);
};
/**
* @description GBK编码的base64解密,解密成为js内置的utf16编码
* 注意:这里会讲传入的字符串当成GBK型,如果类型不匹配,就会产生错误结果
* @param {String} str 传入的GBK编码型base64字符串
* @return {String} 解码后的utf16字符串
*/
base64.decode_GBK = function(str) {
var decodeStr = decodeBase64(str);
// 转成UTF16
return exports.GBKStrToUTF16Str(decodeStr);
};
})(exports.Base64 = {});
return exports;
});