2012
Feb
22

Javascript 的中文编码是使用两个 bytes 来储存的,而英文、符号等是使用一个 byte 来储存。

Using length

使用 string.length 来计算长度时,中文字串会被当成是一个字来计算,因为 Javascript 是使用 multiple byte 计算,就像是 php 的 mb_strlen。

Example
  1. var tx="测试&eng";
  2. alert(tx.length);
  3. //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
Example
  1. for (var i = 0; i < tx.length; i++) {
  2. c+=""+tx.charCodeAt(i).toString(2)+"<br />";
  3. k+=""+tx.charCodeAt(i).toString()+"<br />";
  4. }
  5. document.body.innerHTML+=c;
  6. document.body.innerHTML+=k;
  7. //output
  8. /*
  9. 110111000101100
  10. 1000101001100110
  11. 100111000101101
  12. 110010110000111
  13.  
  14. 28204
  15. 35430
  16. 20013
  17. 25991
  18. */
从上面就可以看出来,虽然我是使用 UTF-8 编码,不过转成二进位后,每个中文字都是两个 bytes 的长度而已(从上面的二进位数字中来看,最多只有 16 个 「0和1」,而一个 byte 有 8 个 bits ,所以是 两个 bytes)。

计算文字 bytes 数

我将每一个文字转成数字,再去计算他的二进位的 bit 数,决定文字是使用几个 byte 来储存。


两个不同的编码页面,使用 length抓到的结果是不相同的,原因是我写的 Javascript 是使用 UTF-8 编码,所以可以正常的计算 UTF-8编码的内容长度,不过在计算 big5 编码的内容时,就会因为编码表的不同,而出现怪怪的结果。

接著我试著在 Javascript 中,把中文字转成二进位的编码来看看。

Example
  1. var tx="测试中文";
  2. var c=""
  3. function stringBytes(c){
  4. var n=c.length,s;
  5. var len=0;
  6. for(var i=0; i <n;i++){
  7. s=c.charCodeAt(i);
  8. while( s > 0 ){
  9. len++;
  10. s = s >> 8;
  11. }
  12. }
  13. return len;
  14. }
  15.  
  16. var tx="测试中文12313";
  17. $(document.body).append(stringBytes(tx));
  18. //output: 13

上个范例中,中文字有四个,数字有五个,每个中文字为 2 bytes ,所以总合长度是 13 bytes 。

不存在三个 bytes 的文字

测试了一下 Javascript 是否有三个 bytes 的文字,结果发现,当编码的数字大於 65535 时(16 bits),数字又会回到 0 开始计算,所以 Javascript 的编码表中最多只有 65536 个文字 (包含 0 , 0 ~ 65535)。

Example
  1. var t1=String.fromCharCode(65586);
  2. var t2=String.fromCharCode(50);
  3. if(t1==t2) alert("yes")
  4. //两个文字都是 2 , (65586 -65536 =50)

Using encodeURIComponent

另一种计算方式是使用 encodeURIComponent ,先使用 encodeURIComponent 将字串进行编码,之后再用 Regular Expression 计算 bytes 数。

Example
  1. tx = "测式中文xxxx";
  2. var str = encodeURIComponent(tx);
  3. console.log("utf8:"+str);
  4. len = str.replace(/%[A-F\d]{2}/g, 'U').length;
  5. console.log("len = "+len);
这个方式可以成功的计算 Big5 & UTF-8 的编码长度。

get string length from web

如果我们使用 ajax 去抓网页的资料时, Big5 编码的页面会自动被转换成 UTF-8 的乱码,这时 encodeURIComponent 就没办法计算正确的长度。

用 ajax 从 header 中抓页面的长度

如果是要抓页面的size ,可以直接从 header 的资讯中取得,方式如下。

Example
  1. //using jquery
  2. var geturl;
  3. geturl =$.ajax({
  4. type:"get",
  5. url:"b.txt",
  6. beforeSend:function(){
  7. },
  8. success:function(tx){
  9. var header = geturl.getAllResponseHeaders();
  10. var re=/Content-Length:[\s]([0-9]+)/;
  11. var k=header.match(re);
  12. document.body.innerHTML="length = "+k[1];
  13. }
  14. });

使用 ajax 取得 header 的内容

在 Ajax 回传的 物件中,使用 method : getAllResponseHeaders,就可以抓到 response header 的内容,范例如下。

Example
  1. var request = null;
  2. try {
  3. request = new XMLHttpRequest();
  4. } catch (trymicrosoft) {
  5. try {
  6. request = new ActiveXObject("Msxml2.XMLHTTP");
  7. } catch (othermicrosoft) {
  8. try {
  9. request = new ActiveXObject("Microsoft.XMLHTTP");
  10. } catch (failed) {
  11. request = null;
  12. }
  13. }
  14. }
  15. function ajaxResultHeader() {
  16.  
  17. if (request.readyState == 4 ) {
  18. if (request.status == 200 ) {
  19. var tx = request.responseText;
  20. var header =request.getAllResponseHeaders();
  21. console.log("header = "+header);
  22. } else
  23. alert("Error! Request status is " + request.status);
  24. }
  25. }
  26. function ajaxGetHeader() {
  27. var url = "b.txt";
  28. request.open("GET" , url, true);
  29. request.onreadystatechange = ajaxResultHeader;
  30. request.setRequestHeader("Content-Type","application/octet-stream");
  31. request.send();
  32. }
  33. ajaxGetHeader();

取得的 header 内容

Example
  1. Date: Wed, 29 Feb 2012 03:01:23 GMT
  2. 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
  3. Last-Modified: Wed, 29 Feb 2012 02:44:38 GMT
  4. Etag: "b00000002859d-827-4ba115319353b"
  5. Accept-Ranges: bytes
  6. Content-Length: 2087
  7. 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
Example
  1. var request = null;
  2. try {
  3. request = new XMLHttpRequest();
  4. } catch (trymicrosoft) {
  5. try {
  6. request = new ActiveXObject("Msxml2.XMLHTTP");
  7. } catch (othermicrosoft) {
  8. try {
  9. request = new ActiveXObject("Microsoft.XMLHTTP");
  10. } catch (failed) {
  11. request = null;
  12. }
  13. }
  14. }
  15. function ajaxResult() {
  16.  
  17. if (request.readyState == 4 ) {
  18. if (request.status == 200 || request.status == 206) {
  19. var tx = request.responseText;
  20. console.log("content= "+tx);
  21. } else
  22. alert("Error! Request status is " + request.status);
  23. }
  24. }
  25. function ajaxGet() {
  26. var url = "b.txt";
  27. request.open("GET" , url, true);
  28. request.onreadystatechange = ajaxResult;
  29. request.setRequestHeader("Content-Type","application/octet-stream");
  30. request.setRequestHeader("Range","bytes=0-2000");
  31. request.send();
  32. }
  33. ajaxGet();

Request Header Example

线上测试,使用 Ajax 抓 page header And content

线上测试
Bytes: from ~

备注

bit 运算

在电脑的世界中,数字都是使用二进位来计算的,下面是基本的十进位与二进位对照表。

十进位二进位
210
4100
81000
6110

二进位运算符号 「>>」,「 >> 1」这个是指二进位的数字向右边移动一格,所以最右边的数字会被删除,范例如下。

十进位运算二进位的原始值二进位结果
2 >> 1 = 1101
11 >> 2 = 3 111011
8 >> 1 = 41000100
6 >> 1 = 311011

目前回應 Comments(1 comments)

  • 數星星 2014/11/11

    文中計算字串長度函式中的這行,目前使用Chrome版本 38.0.2125.111 m 是失效的:
    len = str.replace(/%[A-Fd]{2}/g, 'U').length;
    改為
    len = str.replace(/%[A-F0-9]{2}/g, 'U').length;
    可正常使用

    Reply

    Admin

    因為編輯器的關系 ,程式瀘掉了一個斜線 「 \d」,正確的程式如下
     
    len = str.replace(/%[A-F\d]{2}/g, 'U').length;

回應 (Leave a comment)