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 |