cisen / blog

Time waits for no one.
134 stars 20 forks source link

ASCII、Unicode、UTF-8、base64 #51

Open cisen opened 5 years ago

cisen commented 5 years ago

前提

什么是字符编码?

  计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4个字节可以表示的最大整数是4294967295。

ASCII编码:

  由于计算机是美国人发明的,因此,最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母 A 的编码是65,小写字母 z 的编码是122。   但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。   全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

Unicode编码:

  因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。 ASCII编码和Unicode编码的区别:   1)ASCII编码是1个字节,而Unicode编码通常是2个字节,举例如下。   字母 A 用ASCII编码是十进制的65,二进制的01000001;   字符 0 用ASCII编码是十进制的48,二进制的00110000,注意字符 '0' 和整数 0 是不同的;   汉字 中 已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。   如果把ASCII编码的 A 用Unicode编码,只需要在前面补0就可以,因此, A 的Unicode编码是00000000 01000001。

UTF-8编码:

  新问题的出现:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。   因此,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:

字符 ASCII Unicode UTF-8
A 01000001 00000000 01000001 01000001
- 01001110 00101101 11100100 10111000 10101101

从上面的表格可以发现UTF-8编码一个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。   强调一下,unicode是一种编码方式,和ascii是同一个概念,而UTF-8,UTF-16等是一种存储方式,在存储和传输上节约空间、提高性能的一种编码形式。 计算机系统通用的字符编码工作方式:   在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。   用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:

  浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器: 所以很多网页的源码上会有类似的信息,表示该网页正是用的UTF-8编码。

使用

ascii

将字母转为ascii嘛的方法:

var str = "A";
str.charCodeAt();  // 65
var str1 = 'a';
str1.charCodeAt();  // 97

将ascii码转为对应字母的方法:

var num = 97;
String.fromCharCode(num);  // 'a'
var num1 = 100;
String.fromCharCode(num1);  // 'd'

和hex的转换

function hex2a(hexx) {
    var hex = hexx.toString();//force conversion
    var str = '';
    for (var i = 0; (i < hex.length && hex.substr(i, 2) !== '00'); i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}
hex2a('32343630'); // returns '2460'

function a2hex(str) {
  var arr = [];
  for (var i = 0, l = str.length; i < l; i ++) {
    var hex = Number(str.charCodeAt(i)).toString(16);
    arr.push(hex);
  }
  return arr.join('');
}
a2hex('2460'); //returns 32343630

unicode

JavaScript本身可通过charCodeAt方法得到一个字符的Unicode编码,并通过fromCharCode方法将Unicode编码转换成对应字符。

// 返回字符串第一个字符的 Unicode 编码:
var str = "HELLO WORLD";
var n = str.charCodeAt(0);  // 72
// fromCharCode() 可接受一个指定的 Unicode 值,然后返回一个字符串。
// String.fromCharCode(numX,numX,...,numX)
String.fromCharCode(72,69,76,76,79) // HELLO

utf8

从Unicode到UTF-8的编码方式如下: Unicode编码(十六进制) UTF-8 字节流(二进制)
000000-00007F 0xxxxxxx
000080-0007FF 110xxxxx 10xxxxxx
000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF 11110xxx10xxxxxx10xxxxxx10xxxxxx

以下是js实现代码,首先是编码

function utf8Encode(inputStr) {
  var outputStr = "";

  for(var i = 0; i < inputStr.length; i++) {
    var temp = inputStr.charCodeAt(i);

    //0xxxxxxx
    if(temp < 128) {
      outputStr += String.fromCharCode(temp);
    }
    //110xxxxx 10xxxxxx
    else if(temp < 2048) {
      outputStr += String.fromCharCode((temp >> 6) | 192);
      outputStr += String.fromCharCode((temp & 63) | 128);
    }
    //1110xxxx 10xxxxxx 10xxxxxx
    else if(temp < 65536) {
      outputStr += String.fromCharCode((temp >> 12) | 224);
      outputStr += String.fromCharCode(((temp >> 6) & 63) | 128);
      outputStr += String.fromCharCode((temp & 63) | 128);
    }
    //11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    else {
      outputStr += String.fromCharCode((temp >> 18) | 240);
      outputStr += String.fromCharCode(((temp >> 12) & 63) | 128);
      outputStr += String.fromCharCode(((temp >> 6) & 63) | 128);
      outputStr += String.fromCharCode((temp & 63) | 128);
    }
  }

  return outputStr;
}

下面是解码

function utf8Decode(inputStr) {
  var outputStr = "";
  var code1, code2, code3, code4;

  for(var i = 0; i < inputStr.length; i++) {
    code1 = inputStr.charCodeAt(i);

    if(code1 < 128) {
      outputStr += String.fromCharCode(code1);
    }
    else if(code1 < 224) {
      code2 = inputStr.charCodeAt(++i);
      outputStr += String.fromCharCode(((code1 & 31) << 6) | (code2 & 63));
    }
    else if(code1 < 240) {
      code2 = inputStr.charCodeAt(++i);
      code3 = inputStr.charCodeAt(++i);
      outputStr += String.fromCharCode(((code1 & 15) << 12) | ((code2 & 63) << 6) | (code3 & 63));
    }
    else {
      code2 = inputStr.charCodeAt(++i);
      code3 = inputStr.charCodeAt(++i);
      code4 = inputStr.charCodeAt(++i);
      outputStr += String.fromCharCode(((code1 & 7) << 18) | ((code2 & 63) << 12) |((code3 & 63) << 6) | (code2 & 63));
    }
  }

  return outputStr;
}

unicode 和utf8

//unicode为1个接收数据,串口收到的字符编码放在该数组中
function UnicodeToUtf8(unicode) {
    var uchar;
    var utf8str = "";
    var i;

    for(i=0; i<unicode.length;i+=2){            
        uchar = (unicode[i]<<8) | unicode[i+1];             //UNICODE为2字节编码,一次读入2个字节
        utf8str = utf8str  + String.fromCharCode(uchar);    //使用String.fromCharCode强制转换
    }
    return utf8str;
}

function Utf8ToUnicode(strUtf8) {
    var i,j;
    var uCode;
    var temp = new Array();

    for(i=0,j=0; i<strUtf8.length; i++){
        uCode = strUtf8.charCodeAt(i);
        if(uCode<0x100){                    //ASCII字符
            temp[j++] = 0x00;
            temp[j++] = uCode;
        }else if(uCode<0x10000){
            temp[j++] = (uCode>>8)&0xff;
            temp[j++] = uCode&0xff;
        }else if(uCode<0x1000000){
            temp[j++] = (uCode>>16)&0xff;
            temp[j++] = (uCode>>8)&0xff;
            temp[j++] = uCode&0xff;
        }else if(uCode<0x100000000){
            temp[j++] = (uCode>>24)&0xff;
            temp[j++] = (uCode>>16)&0xff;
            temp[j++] = (uCode>>8)&0xff;
            temp[j++] = uCode&0xff;
        }else{
            break;
        }
    }
    temp.length = j;
    return temp;
}

Base64

百度百科中对Base64有一个很好的解释:“Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法”。

什么是“可打印字符”呢?为什么要用它来传输8Bit字节码呢?在回答这两个问题之前我们有必要来思考一下什么情况下需要使用到Base64?Base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符。什么是可打印字符?在ASCII码中规定,0~31、128这33个字符属于控制字符,32~127这95个字符属于可打印字符,也就是说网络传输只能传输这95个字符,不在这个范围内的字符无法传输。那么该怎么才能传输其他字符呢?其中一种方式就是使用Base64。

因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就不能通过邮件传送。这样就受到了很大的限制,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。最好的方法就是在不改变传统协议的情况下,开辟一种新的方案来支持二进制文件的传送。把不可见字符用可见字符来表示。而Base64就是一种基于64个可见字符来表示二进制数据的表示方法。

扩展:不可见字符其实并不是不显示,只是这些字符在屏幕上显示不出来,比如:换行符、回车、退格......字符。

Base64,就是使用64个可打印字符来表示二进制数据的方法。Base64的索引与对应字符的关系如下表所示:

转化

把abc这三个字符转换为Base64的过程

字符串      a       b        c
ASCII      97      98       99
8bit   01100001 01100010 01100011
6bit   011000   010110   001001   100011
十进制      24      22        9        35
对应编码    Y        W        J        j

  把man这三个字符转换为Base64的过程

字符串    m         a        n
ASCII    109       97       110
8bit  01101101 01100001 01101110
6bit  011011   010110   000101     101110
十进制     27       22       5         46
对应编码   b        W        F          u
cisen commented 5 years ago

Unicode字符编码表

https://www.cnblogs.com/chris-oil/p/8677309.html

十进制 十六进制 字符数 编码分类(中文) 编码分类(英文)
起始 终止 起始 终止 (个) 说明   说明  
0 127 0000 007F 128 C0控制符及基本拉丁文 C0 Control and Basic Latin
128 255 0080 00FF 128 C1控制符及拉丁文补充-1 C1 Control and Latin 1 Supplement
256 383 0100 017F 128 拉丁文扩展-A Latin Extended-A
384 591 0180 024F 208 拉丁文扩展-B Latin Extended-B
592 687 0250 02AF 96 国际音标扩展 IPA Extensions
688 767 02B0 02FF 80 空白修饰字母 Spacing Modifiers
768 879 0300 036F 112 结合用读音符号 Combining Diacritics Marks
880 1023 0370 03FF 144 希腊文及科普特文 Greek and Coptic
1024 1279 0400 04FF 256 西里尔字母 Cyrillic
1280 1327 0500 052F 48 西里尔字母补充 Cyrillic Supplement
1328 1423 0530 058F 96 亚美尼亚语 Armenian
1424 1535 0590 05FF 112 希伯来文 Hebrew
1536 1791 0600 06FF 256 阿拉伯文 Arabic
1792 1871 0700 074F 80 叙利亚文 Syriac
1872 1919 0750 077F 48 阿拉伯文补充 Arabic Supplement
1920 1983 0780 07BF 64 马尔代夫语 Thaana
1984 2047 07C0 07FF 64 西非書面語言 N'Ko
2048 2143 0800 085F 96 阿维斯塔语及巴列维语 Avestan and Pahlavi
2144 2175 0860 087F 32 Mandaic Mandaic
2176 2223 0880 08AF 48 撒马利亚语 Samaritan
2304 2431 0900 097F 128 天城文书 Devanagari
2432 2559 0980 09FF 128 孟加拉语 Bengali
2560 2687 0A00 0A7F 128 锡克教文 Gurmukhi
2688 2815 0A80 0AFF 128 古吉拉特文 Gujarati
2816 2943 0B00 0B7F 128 奥里亚文 Oriya
2944 3071 0B80 0BFF 128 泰米尔文 Tamil
3072 3199 0C00 0C7F 128 泰卢固文 Telugu
3200 3327 0C80 0CFF 128 卡纳达文 Kannada
3328 3455 0D00 0D7F 128 德拉维族语 Malayalam
3456 3583 0D80 0DFF 128 僧伽罗语 Sinhala
3584 3711 0E00 0E7F 128 泰文 Thai
3712 3839 0E80 0EFF 128 老挝文 Lao
3840 4095 0F00 0FFF 256 藏文 Tibetan
4096 4255 1000 109F 160 缅甸语 Myanmar
4256 4351 10A0 10FF 96 格鲁吉亚语 Georgian
4352 4607 1100 11FF 256 朝鲜文 Hangul Jamo
4608 4991 1200 137F 384 埃塞俄比亚语 Ethiopic
4992 5023 1380 139F 32 埃塞俄比亚语补充 Ethiopic Supplement
5024 5119 13A0 13FF 96 切罗基语 Cherokee
5120 5759 1400 167F 640 统一加拿大土著语音节 Unified Canadian Aboriginal Syllabics
5760 5791 1680 169F 32 欧甘字母 Ogham
5792 5887 16A0 16FF 96 如尼文 Runic
5888 5919 1700 171F 32 塔加拉语 Tagalog
5920 5951 1720 173F 32 Hanunóo Hanunóo
5952 5983 1740 175F 32 Buhid Buhid
5984 6015 1760 177F 32 Tagbanwa Tagbanwa
6016 6143 1780 17FF 128 高棉语 Khmer
6144 6319 1800 18AF 176 蒙古文 Mongolian
6320 6399 18B0 18FF 80 Cham Cham
6400 6479 1900 194F 80 Limbu Limbu
6480 6527 1950 197F 48 德宏泰语 Tai Le
6528 6623 1980 19DF 96 新傣仂语 New Tai Lue
6624 6655 19E0 19FF 32 高棉语记号 Kmer Symbols
6656 6687 1A00 1A1F 32 Buginese Buginese
6688 6751 1A20 1A5F 64 Batak Batak
6784 6895 1A80 1AEF 112 Lanna Lanna
6912 7039 1B00 1B7F 128 巴厘语 Balinese
7040 7088 1B80 1BB0 49 巽他语 Sundanese
7104 7167 1BC0 1BFF 64 Pahawh Hmong Pahawh Hmong
7168 7247 1C00 1C4F 80 雷布查语 Lepcha
7248 7295 1C50 1C7F 48 Ol Chiki Ol Chiki
7296 7391 1C80 1CDF 96 曼尼普尔语 Meithei/Manipuri
7424 7551 1D00 1D7F 128 语音学扩展 Phonetic Extensions
7552 7615 1D80 1DBF 64 语音学扩展补充 Phonetic Extensions Supplement
7616 7679 1DC0 1DFF 64 结合用读音符号补充 Combining Diacritics Marks Supplement
7680 7935 1E00 1EFF 256 拉丁文扩充附加 Latin Extended Additional
7936 8191 1F00 1FFF 256 希腊语扩充 Greek Extended
8192 8303 2000 206F 112 常用标点 General Punctuation
8304 8351 2070 209F 48 上标及下标 Superscripts and Subscripts
8352 8399 20A0 20CF 48 货币符号 Currency Symbols
8400 8447 20D0 20FF 48 组合用记号 Combining Diacritics Marks for Symbols
8448 8527 2100 214F 80 字母式符号 Letterlike Symbols
8528 8591 2150 218F 64 数字形式 Number Form
8592 8703 2190 21FF 112 箭头 Arrows
8704 8959 2200 22FF 256 数学运算符 Mathematical Operator
8960 9215 2300 23FF 256 杂项工业符号 Miscellaneous Technical
9216 9279 2400 243F 64 控制图片 Control Pictures
9280 9311 2440 245F 32 光学识别符 Optical Character Recognition
9312 9471 2460 24FF 160 封闭式字母数字 Enclosed Alphanumerics
9472 9599 2500 257F 128 制表符 Box Drawing
9600 9631 2580 259F 32 方块元素 Block Element
9632 9727 25A0 25FF 96 几何图形 Geometric Shapes
9728 9983 2600 26FF 256 杂项符号 Miscellaneous Symbols
9984 10175 2700 27BF 192 印刷符号 Dingbats
10176 10223 27C0 27EF 48 杂项数学符号-A Miscellaneous Mathematical Symbols-A
10224 10239 27F0 27FF 16 追加箭头-A Supplemental Arrows-A
10240 10495 2800 28FF 256 盲文点字模型 Braille Patterns
10496 10623 2900 297F 128 追加箭头-B Supplemental Arrows-B
10624 10751 2980 29FF 128 杂项数学符号-B Miscellaneous Mathematical Symbols-B
10752 11007 2A00 2AFF 256 追加数学运算符 Supplemental Mathematical Operator
11008 11263 2B00 2BFF 256 杂项符号和箭头 Miscellaneous Symbols and Arrows
11264 11359 2C00 2C5F 96 格拉哥里字母 Glagolitic
11360 11391 2C60 2C7F 32 拉丁文扩展-C Latin Extended-C
11392 11519 2C80 2CFF 128 古埃及语 Coptic
11520 11567 2D00 2D2F 48 格鲁吉亚语补充 Georgian Supplement
11568 11647 2D30 2D7F 80 提非纳文 Tifinagh
11648 11743 2D80 2DDF 96 埃塞俄比亚语扩展 Ethiopic Extended
11776 11903 2E00 2E7F 128 追加标点 Supplemental Punctuation
11904 12031 2E80 2EFF 128 CJK 部首补充 CJK Radicals Supplement
12032 12255 2F00 2FDF 224 康熙字典部首 Kangxi Radicals
12272 12287 2FF0 2FFF 16 表意文字描述符 Ideographic Description Characters
12288 12351 3000 303F 64 CJK 符号和标点 CJK Symbols and Punctuation
12352 12447 3040 309F 96 日文平假名 Hiragana
12448 12543 30A0 30FF 96 日文片假名 Katakana
12544 12591 3100 312F 48 注音字母 Bopomofo
12592 12687 3130 318F 96 朝鲜文兼容字母 Hangul Compatibility Jamo
12688 12703 3190 319F 16 象形字注释标志 Kanbun
12704 12735 31A0 31BF 32 注音字母扩展 Bopomofo Extended
12736 12783 31C0 31EF 48 CJK 笔画 CJK Strokes
12784 12799 31F0 31FF 16 日文片假名语音扩展 Katakana Phonetic Extensions
12800 13055 3200 32FF 256 封闭式 CJK 文字和月份 Enclosed CJK Letters and Months
13056 13311 3300 33FF 256 CJK 兼容 CJK Compatibility
13312 19903 3400 4DBF 6592 CJK 统一表意符号扩展 A CJK Unified Ideographs Extension A
19904 19967 4DC0 4DFF 64 易经六十四卦符号 Yijing Hexagrams Symbols
19968 40895 4E00 9FBF 20928 CJK 统一表意符号 CJK Unified Ideographs
40960 42127 A000 A48F 1168 彝文音节 Yi Syllables
42128 42191 A490 A4CF 64 彝文字根 Yi Radicals
42240 42527 A500 A61F 288 Vai Vai
42592 42751 A660 A6FF 160 统一加拿大土著语音节补充 Unified Canadian Aboriginal Syllabics Supplement
42752 42783 A700 A71F 32 声调修饰字母 Modifier Tone Letters
42784 43007 A720 A7FF 224 拉丁文扩展-D Latin Extended-D
43008 43055 A800 A82F 48 Syloti Nagri Syloti Nagri
43072 43135 A840 A87F 64 八思巴字 Phags-pa
43136 43231 A880 A8DF 96 Saurashtra Saurashtra
43264 43391 A900 A97F 128 爪哇语 Javanese
43392 43487 A980 A9DF 96 Chakma Chakma
43520 43583 AA00 AA3F 64 Varang Kshiti Varang Kshiti
43584 43631 AA40 AA6F 48 Sorang Sompeng Sorang Sompeng
43648 43743 AA80 AADF 96 Newari Newari
43776 43871 AB00 AB5F 96 越南傣语 Vi?t Thái
43904 43936 AB80 ABA0 33 Kayah Li Kayah Li
44032 55215 AC00 D7AF 11184 朝鲜文音节 Hangul Syllables
55296 56319 D800 DBFF 1024 High-half zone of UTF-16 High-half zone of UTF-16
56320 57343 DC00 DFFF 1024 Low-half zone of UTF-16 Low-half zone of UTF-16
57344 63743 E000 F8FF 6400 自行使用區域 Private Use Zone
63744 64255 F900 FAFF 512 CJK 兼容象形文字 CJK Compatibility Ideographs
64256 64335 FB00 FB4F 80 字母表達形式 Alphabetic Presentation Form
64336 65023 FB50 FDFF 688 阿拉伯表達形式A Arabic Presentation Form-A
65024 65039 FE00 FE0F 16 变量选择符 Variation Selector
65040 65055 FE10 FE1F 16 竖排形式 Vertical Forms
65056 65071 FE20 FE2F 16 组合用半符号 Combining Half Marks
65072 65103 FE30 FE4F 32 CJK 兼容形式 CJK Compatibility Forms
65104 65135 FE50 FE6F 32 小型变体形式 Small Form Variants
65136 65279 FE70 FEFF 144 阿拉伯表達形式B Arabic Presentation Form-B
65280 65519 FF00 FFEF 240 半型及全型形式 Halfwidth and Fullwidth Form
65520 65535 FFF0 FFFF 16 特殊 Specials

UTF-8有点类似于Haffman编码,它将Unicode编码为: 0x00-0x7F的字符,用单个字节来表示; 0x80-0x7FF的字符用两个字节表示; 0x800-0xFFFF的字符用3字节表示; 汉字的unicode范围是:0x4E00~0x9FA5 其实这个范围还包括了中,日,韩的字符。

14ce36d3d539b60046b9e5b8e950352ac65cb74e