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 |