Javascript 的中文编码是使用两个 bytes 来储存的,而英文、符号等是使用一个 byte 来储存。
Using length
使用 string.length 来计算长度时,中文字串会被当成是一个字来计算,因为 Javascript 是使用 multiple byte 计算,就像是 php 的 mb_strlen。
- var tx="测试&eng";
 - alert(tx.length);
 - //output: 6
 
看到吧不管是中文,英文,都当成一个字来算,所以总共是 6 个字,不过有时候我们并不想把中文字当成一个字来计算,因为中文字明明占的 byte 数就是比较多,所以来看一下中文编码吧。
中文编码
一般使用 big5 为繁体中文编码,每一个中文字是使用两个 bytes 来储存。
UTF-8 编码则是每一个中文字使用三个 bytes 来储存。
不过这个算法对 javascript 的中文长度计算是有问题的,首先我写了一小段 Javascript 程式码,以及两个不同编码的网页,我先抓取一段 UTF-8编码的网页,然后计算他的长度。
utf8_data.txt 的内容 = [测试中文eng] ,这个档案在 linux 系统中看到的档案大小为 15 bytes,代表一个中文字使用三个 bytes 储存,不过在 Javascript中抓到的字串长度 = 7。
big5_data.txt 的内容 = [测试中文eng],这个档案在 linux 系统中看到的档案大小为 11 bytes,代表一个中文字使用二个 bytes 储存,不过在 Javascript中抓到的字串长度 = 9
View Demo
- for (var i = 0; i < tx.length; i++) {
 - c+=""+tx.charCodeAt(i).toString(2)+"<br />";
 - k+=""+tx.charCodeAt(i).toString()+"<br />";
 - }
 - document.body.innerHTML+=c;
 - document.body.innerHTML+=k;
 - //output
 - /*
 - 110111000101100
 - 1000101001100110
 - 100111000101101
 - 110010110000111
 - 28204
 - 35430
 - 20013
 - 25991
 - */
 
计算文字 bytes 数
我将每一个文字转成数字,再去计算他的二进位的 bit 数,决定文字是使用几个 byte 来储存。
两个不同的编码页面,使用 length抓到的结果是不相同的,原因是我写的 Javascript 是使用 UTF-8 编码,所以可以正常的计算 UTF-8编码的内容长度,不过在计算 big5 编码的内容时,就会因为编码表的不同,而出现怪怪的结果。
接著我试著在 Javascript 中,把中文字转成二进位的编码来看看。
- var tx="测试中文";
 - var c=""
 - function stringBytes(c){
 - var n=c.length,s;
 - var len=0;
 - for(var i=0; i <n;i++){
 - s=c.charCodeAt(i);
 - while( s > 0 ){
 - len++;
 - s = s >> 8;
 - }
 - }
 - return len;
 - }
 - var tx="测试中文12313";
 - $(document.body).append(stringBytes(tx));
 - //output: 13
 
上个范例中,中文字有四个,数字有五个,每个中文字为 2 bytes ,所以总合长度是 13 bytes 。
不存在三个 bytes 的文字
测试了一下 Javascript 是否有三个 bytes 的文字,结果发现,当编码的数字大於 65535 时(16 bits),数字又会回到 0 开始计算,所以 Javascript 的编码表中最多只有 65536 个文字 (包含 0 , 0 ~ 65535)。
- var t1=String.fromCharCode(65586);
 - var t2=String.fromCharCode(50);
 - if(t1==t2) alert("yes")
 - //两个文字都是 2 , (65586 -65536 =50)
 
- http://www.puritys.me/docs-blog/article-32 : 这里可以输出 HTML 编码表, 从数字 19968 后开始,就是中文字的编码表。
 
Using encodeURIComponent
另一种计算方式是使用 encodeURIComponent ,先使用 encodeURIComponent 将字串进行编码,之后再用 Regular Expression 计算 bytes 数。
- tx = "测式中文xxxx";
 - var str = encodeURIComponent(tx);
 - console.log("utf8:"+str);
 - len = str.replace(/%[A-F\d]{2}/g, 'U').length;
 - console.log("len = "+len);
 
get string length from web
如果我们使用 ajax 去抓网页的资料时, Big5 编码的页面会自动被转换成 UTF-8 的乱码,这时 encodeURIComponent 就没办法计算正确的长度。
用 ajax 从 header 中抓页面的长度
如果是要抓页面的size ,可以直接从 header 的资讯中取得,方式如下。
- //using jquery
 - var geturl;
 - geturl =$.ajax({
 - type:"get",
 - url:"b.txt",
 - beforeSend:function(){
 - },
 - success:function(tx){
 - var header = geturl.getAllResponseHeaders();
 - var re=/Content-Length:[\s]([0-9]+)/;
 - var k=header.match(re);
 - document.body.innerHTML="length = "+k[1];
 - }
 - });
 
使用 ajax 取得 header 的内容
在 Ajax 回传的 物件中,使用 method : getAllResponseHeaders,就可以抓到 response header 的内容,范例如下。
- var request = null;
 - try {
 - request = new XMLHttpRequest();
 - } catch (trymicrosoft) {
 - try {
 - request = new ActiveXObject("Msxml2.XMLHTTP");
 - } catch (othermicrosoft) {
 - try {
 - request = new ActiveXObject("Microsoft.XMLHTTP");
 - } catch (failed) {
 - request = null;
 - }
 - }
 - }
 - function ajaxResultHeader() {
 - if (request.readyState == 4 ) {
 - if (request.status == 200 ) {
 - var tx = request.responseText;
 - var header =request.getAllResponseHeaders();
 - console.log("header = "+header);
 - } else
 - alert("Error! Request status is " + request.status);
 - }
 - }
 - function ajaxGetHeader() {
 - var url = "b.txt";
 - request.open("GET" , url, true);
 - request.onreadystatechange = ajaxResultHeader;
 - request.setRequestHeader("Content-Type","application/octet-stream");
 - request.send();
 - }
 - ajaxGetHeader();
 
取得的 header 内容
- Date: Wed, 29 Feb 2012 03:01:23 GMT
 - Server: Apache/2.2.14 (Win32) DAV/2 mod_ssl/2.2.14 OpenSSL/0.9.8l mod_autoindex_color PHP/5.3.1 mod_apreq2-20090110/2.7.1 mod_perl/2.0.4 Perl/v5.10.1
 - Last-Modified: Wed, 29 Feb 2012 02:44:38 GMT
 - Etag: "b00000002859d-827-4ba115319353b"
 - Accept-Ranges: bytes
 - Content-Length: 2087
 - Content-Type: text/plain
 
使用 ajax 从 content 中取得前 2KB 的Bytes 数
HTTP Request 中本身有个属性 Range , 可以指定我要取得多少 bytes ,从 x1 ~ x2 bytes ,因为从 Server 端取得的资料就是 2 KB ,这样就不需要经过 Ajax 的胡乱编码过程,就能正确的取得bytes数,用法就是在 Request Header 中加入 Range:bytes=0-2000,下面是范例。
! 使用 Range 抓取片段资料,Http Response 的 status 会等於 206
- var request = null;
 - try {
 - request = new XMLHttpRequest();
 - } catch (trymicrosoft) {
 - try {
 - request = new ActiveXObject("Msxml2.XMLHTTP");
 - } catch (othermicrosoft) {
 - try {
 - request = new ActiveXObject("Microsoft.XMLHTTP");
 - } catch (failed) {
 - request = null;
 - }
 - }
 - }
 - function ajaxResult() {
 - if (request.readyState == 4 ) {
 - if (request.status == 200 || request.status == 206) {
 - var tx = request.responseText;
 - console.log("content= "+tx);
 - } else
 - alert("Error! Request status is " + request.status);
 - }
 - }
 - function ajaxGet() {
 - var url = "b.txt";
 - request.open("GET" , url, true);
 - request.onreadystatechange = ajaxResult;
 - request.setRequestHeader("Content-Type","application/octet-stream");
 - request.setRequestHeader("Range","bytes=0-2000");
 - request.send();
 - }
 - ajaxGet();
 
Request Header Example
线上测试,使用 Ajax 抓 page header And content
备注
bit 运算
在电脑的世界中,数字都是使用二进位来计算的,下面是基本的十进位与二进位对照表。
| 十进位 | 二进位 | 
| 2 | 10 | 
| 4 | 100 | 
| 8 | 1000 | 
| 6 | 110 | 
二进位运算符号 「>>」,「 >> 1」这个是指二进位的数字向右边移动一格,所以最右边的数字会被删除,范例如下。
| 十进位运算 | 二进位的原始值 | 二进位结果 | 
| 2 >> 1 = 1 | 10 | 1 | 
| 11 >> 2 = 3 | 1110 | 11 | 
| 8 >> 1 = 4 | 1000 | 100 | 
| 6 >> 1 = 3 | 110 | 11 | 
