JS实现UTF8编解码及Base64编解码
来源:互联网 发布:空难 尼尔森 知乎 编辑:程序博客网 时间:2024/04/29 18:30
最近抽了点时间去了解了下Unicode,UTF-8,Base64之间的关系,以及它们之间 的一些转换规则,并且自己动手按照相应的编码规则实现了相应的编解码,虽然写的很生硬,没有网上一些大神写的那么简洁,编解码效率可能也不那么高,但是我还是决定把我自己的实现思路分享一下,希望可以为那些想了解具体编码规则及过程的网友有一定的帮助,另外也希望各位大神指点指点,看看如何实现编解码可以让代码更高效更简洁。
对于Unicode,UTF-8,Base64的具体定义本文不会过多解释,因为网上一搜一大把,本文主要讲编解码的规则和实现。以下内容便是按照相应规则对UTF-8,Base64的编解码实现。对于以下代码我已经进行了初步的验证,可正常编解码,各位网友可以直接拷贝使用,如有疑问或是代码有问题,请留言,我会尽快回复并跟进,谢谢!
/*
百度百科对UTF-8的定义:UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,又称万国码。由Ken Thompson于1992年创建。现在已经标准化为RFC 3629。UTF-8用1到6个字节编码Unicode字符。用在网页上可以统一页面显示中文简体繁体及其它语言(如英文,日文,韩文),简单来说就是UTF-8是Unicode的一种实现方式。
Unicode符号范围 | UTF-8编码方式
(十六进制) | (十进制) | (二进制)--------------------+---------------------------------------------
0000 0000-0000 007F | 0-127 | 0xxxxxxx
0000 0080-0000 07FF | 128-2047 | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 2048-65535 | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 65536-1114111 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
UTF-8的编码规则只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。x表示可用编码的位。
Unicode符号范围 | UTF-8编码方式
*/
function UTF8()
{
this.encode = function (string) {
var utftext = "";
var byte = [];
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);//获取对应的unicode
if (c < 128) {
utftext += '\\x' + c.toString(16).toUpperCase();//单字节字符
byte.push(c);
}
else {
var byte_count = 2;
if (c > 127 && c < 2048)
byte_count = 2;
else if (c > 2047 && c < 65536)
byte_count = 3;
else if (c > 65535 && c < 1114112)
byte_count = 4;
else
return "编码失败!仅支持4位字节及以下的字符串编码!";
var height_code = '';
for (var j = 0; j < 8; j++) {
if (j < byte_count)
height_code += '1';
else
height_code += '0';
}
var d_code = parseInt(height_code, 2);
for (var i = byte_count - 1; i > -1; i--)
{
var bit = i * 6;
if (i == byte_count - 1) {
var code = ((c >> bit) | d_code);
utftext += '\\x' + code.toString(16).toUpperCase();//按位右移6位获取到需要与高8位11000000也就是192进行补位的位数,然后通过按位或运算进行补位
byte.push(code)
}
else {
//63的二进制数是00000000 00111111,通过按位与运算获取后6位需要与低8位10000000进行补位的位数,然后或运算补位
var code = (((c >> bit) & 63) | 128);
utftext += '\\x' + code.toString(16).toUpperCase();
byte.push(code);
}
}
}
}
return utftext;
}
this.decode = function (utftext) {
var string = "";
//将utf8字符串分割成单个的十六进制
var split = utftext.split('\\x');
//删除第一个空字符串
split.remove('');
//对分割好的十六进制按照编码规则分组
var group = [];
var complete_binary_code = '';
var count = -1;
for (var i = 0; i < split.length; i++)
{
var str = split[i];
var number = parseInt(str, 16);//转为10进制
var binary = number.toString(2);//转为二进制字符串
//不足8位的要进行补零
for (var j = 0; j < 8 - binary.length; j++)
{
binary = binary.insert(0, '0');
}
var index = binary.indexOf('0');//查找第一个0出现的位置,也就是该组utf8的字节数
if (index > 0) {
if ((index > count && count != -1 && complete_binary_code != ''))
{
//index > count 表明是第二个分组了,但是要排除count==-1的情况,因为这是循环的开始
group.push(complete_binary_code);
complete_binary_code = '';
}
else if (i == split.length - 1) {
//最后一个元素
complete_binary_code += binary.substr(index + 1, 7 - index);
group.push(complete_binary_code);
}
complete_binary_code += binary.substr(index + 1, 7 - index);
count = index;
}
else {
if (count != -1 && complete_binary_code != '')
group.push(complete_binary_code);//上一组的数据需要先保存
complete_binary_code = binary.substr(1, 7);
group.push(complete_binary_code);
complete_binary_code = '';
}
}
for (var i = 0; i < group.length; i++)
{
var binary_str = group[i];
var char_code = parseInt(binary_str, 2);
string += String.fromCharCode(char_code);
}
return string;
}
}
/*
一、维基百科对Base64的定义是:Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。三个字节有24个比特,对应于4个Base64单元,即3个字节可表示4个可打印字符(个人觉得更准确的说应该是3个字节对应4个可打印字符,4个可打印字符可表示3个字节,
因为是based64对字节进行编码,说3个字节表示4个可打印字符不太恰当)。
二、Base64编码字符包括大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符,等号“=”用来作为后缀用途。
三、编码规则:文本转为unicode码->unicode码转为二进制数->二进制数每每6位转换为十进制的索引->使用索引在base64字符表中查找对应的字符。
四、思考:
1.为什么要每6个比特对应一个字符,为什么不是4个或8个?因为二进制转base64是通过索引表的方式进行转换的,索引为0-63,那多少位二进制含括这些索引呢?
根据二进制转十进制规则可以知道,4个二进制数只能表示0-16的数字,所以不符合要求,而6个二进制数可以表示0-64的数字,8个二进制数可以表示0-256的数字,
这两个都可以含括全部索引,但是8位或更多的二进制数来表示的话显然是在浪费资源,所以相比之下每6个比特为一个单元,对应一个字符是最合理的。
2.为什么用4个可打印字符表示3个字节?因为编码后的base64对应的比特位数肯定和编码前的原字符对应的比特位数是一样的,而一个base64字符对应6个比特,一个字节对应8个比特,也就是说
8*字节=6*base64字符,得出base64:字节=4:3,也就是只有每4个可打印字符和3个字节一一对应才能进行正常的解密。不然解码出来的数据就和原来的数据不一致了。
*/
function Base64() {
var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var utf8 = new UTF8();
// public method for encoding
this.encode = function (input) {
//转为utf8获取对应的字节数
var utf8_str = utf8.encode(input);
var bytes = utf8_str.split('\\x');
//删除第一个空字符串
bytes.remove('');
var binary_code = "";
for (var i = 0; i < bytes.length; i++)
{
//转成10进制
var number = parseInt(bytes[i], 16);
//转成2进制
var baniry = number.toString(2);
//不足8位的进行补位
var length = baniry.length;
for (var j = 0; j < 8 - length; j++) {
baniry = baniry.insert(0, "0");
}
binary_code += baniry;
}
var output = "";
//获取based64字符数
var base64_count = Math.ceil(binary_code.length / 6);
var byte_remainder = binary_code.length % 6;
byte_remainder = byte_remainder == 0 ? 6 : byte_remainder;
for (var j = 0; j < (6 - byte_remainder) ; j++)
{
//不足6位在后面追加0
binary_code += "0";
}
var i = 0;
var dec_number = parseInt(binary_code, 2);
var base64_remainder = base64_count % 4;
base64_remainder = base64_remainder == 0 ? 4 : base64_remainder;
while (i < (base64_count + (4 - base64_remainder)))
{
//if (i < (4 - base64_remainder))
// output += "=";
//else
//{
// //思路一:采用逆序的处理逻辑进行编码,每次向右按位移动4 - base64_remainder位,再与63即(111111)进行按位与运算得到对应的数
// //var bit = 6 * (i - (4 - base64_remainder));
// //var index = (dec_number >> bit) & 63;
// //var base64_code = _keyStr.substr(index, 1);
// //output = output.insert(0, base64_code);
// /*
// 注意:js的按位移动操作存在一个问题,对在int的范围(-2147483648~2147483647)内的十进制数进行按位移动操作是正常的
// 如:
// var a = 2147483647;
// var s = a.toString(2);
// console.log(s);输出:1111111111111111111111111111111
// var a = 2147483647 >> 0;
// var s = a.toString(2);
// console.log(s);输出:1111111111111111111111111111111
// 但是当十进制数超出了int的范围就会出问题,比如向右移动0位,按道理来说二进制数是不应该发生变化,但是结果却出乎我的意料
// 如:
// var a = 26141406784;
// var s = a.toString(2);
// console.log(s); 输出:11000010110001001100011011001000000
// 但是如果对其进行按位移动操作之后就会出问题了
// var a = 26141406784 >> 0;
// var s = a.toString(2);
// console.log(s); 输出:10110001001100011011001000000 这里的前面6位莫名奇妙的消失了,最大的可能就是js的按位运算
// 再如:
// var a = -2147483649;
// var s = a.toString(2);
// console.log(s);输出:-10000000000000000000000000000001
// var a = -2147483649 >> 0;
// var s = a.toString(2);
// console.log(s);输出:1111111111111111111111111111111 what?这是什么鬼!!
// 由于以上这个问题,思路一的方式是行不通的
// */
//}
//i++;
//思路二:为要避免以上问题,我选择了截取对应的字符串的方式进行编码
if (i >= base64_count)
output += "=";
else {
var idx = 6 * i;
var code = binary_code.substr(idx, 6);
var index = parseInt(code, 2);
var base64_code = _keyStr.substr(index, 1);
output += base64_code;
}
i++;
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
i = 0;
var binary_code = "";
while (i < input.length)
{
var char = input.substr(i, 1);
var index = _keyStr.indexOf(char);
if (index == 64)
break;
var binary = index.toString(2);
var length = binary.length;
for (var j = 0; j < 6 - length; j++)
{
binary = binary.insert(0, "0");
}
binary_code += binary;
i++;
}
var utf8_array = [];
var byte_count = Math.floor(binary_code.length / 8);
for (var j = 0; j < byte_count; j++) {
var code = binary_code.substr(j * 8, 8);
var dec_code = parseInt(code, 2);
utf8_array.push(dec_code.toString(16));
}
var utf8_str = utf8_array.join('\\x');
output = utf8.decode(utf8_str);
return output;
}
}
//============================js Extention=================================
Array.prototype.indexOf = function (val) {
for (var i = 0; i < this.length; i++) {
if (this[i] == val) return i;
}
return -1;
};
Array.prototype.remove = function (val) {
var index = this.indexOf(val);
if (index > -1) {
this.splice(index, 1);
}
};
String.prototype.insert = function (index, string) {
if (index> 0)
return this.substring(0, index) + string + this.substring(index, this.length);
else
return string + this;
};
阅读全文
0 0
- JS实现UTF8编解码及Base64编解码
- Base64编解码(JS)
- BASE64编解码简单实现
- Java实现BASE64编解码
- VB实现Base64 编解码
- Java实现BASE64编解码
- Java实现BASE64编解码
- java实现Base64编解码
- UCS2 UTF8 编解码
- utf8编解码详解
- VB Base64编解码
- Base64编解码原理
- BASE64编解码
- openssl base64 编、解码
- base64编解码
- Java:Base64编解码
- Base64编解码
- Base64编解码(C)
- 自定义CString类
- 第四场个人训练赛(未完成)
- Ajax
- 牛客《剑指Offer》 跳台阶
- 文章标题
- JS实现UTF8编解码及Base64编解码
- jsp
- poj dfs之2676 Sudoku
- JavaScript中的函数
- 前端九段,你是哪一段?
- Python学习
- 1204: 华科版C语言程序设计教程(第二版)课后习题3.8
- java总结系列之四(内存)
- typeof VS instanceof