字符串、unicode和UTF8編碼之間的互相轉換

轉載請註明文章出處: https://tlanyan.me/string-uni...

想知道某個字符串的UTF8編碼,圖方便打算使用在線工具。坑爹的是,號稱「UTF8漢字互轉」的網頁幾乎全是字符串和unicode碼點互轉,並不提供與UTF8編碼互轉功能。沒搞懂unicode碼點(code point)和UTF8編碼的關係,還大言不慚的說UTF8編碼,真讓人無語。app

搜索結果

轉換示例

字符、字節和字節序,unicode和UTF8編碼,是理解字符編碼重要的概念,詳情可查看本人以前博文文件和字符編碼。本文討論unicode和UTF8之間的轉換,先簡要介紹兩個概念:unicode是將字符與碼點(code point,一個整數)一一對應的編碼方案;碼點一般用uXXXX或者U+XXXX的方式表示,XXXX是碼點的十六進制;UTF8是unicode的一個具體編碼方案,規定字符存儲的方式;UTF8編碼字節數可變,不存在大小端問題,互聯網通訊中常採用此種編碼方式。函數

回顧一下那成片的「漢字UTF8編碼互轉」網頁所作的事情。以「中國」爲例,兩個漢字碼點分別爲20013和22269,十六進制表示4E2D 56FD,UTF8編碼E4 B8 AD E5 9B BD。隨手打開搜索結果中的http://www.ip138.com/utf8/,輸入「中國」,點擊「轉換UTF-8」按鈕,下方出現碼點的十六進制編碼4E2D 56FD(能夠理解爲十六進制的前綴0x),並不是UTF8編碼。工具

JavaScript的String對象有charCodeAtcodePointAt(兼容性不如charCodeAt)方法。根據這個函數,網頁的轉換工做可用以下代碼實現:編碼

function encode(str) {
    var result = '';
    var len = str.length;
    for (var i = 0; i < len; ++ i) {
        result += '&#x' + (str.charCodeAt(i)).toString(16) + ';';    // 轉換成16進制,並增長分隔符
    }
    return result;
}

function decode(str) {
  return String.fromCharCode.apply(null, str.split(';').map(function(un) {
    return parseInt(un.replace('&#x', ''), 16);      // 移除分隔符,轉換成碼點
  }).filter(function (num) {
    return !isNaN(num);
  }));
}

調用encode(「中國」)或者其餘字符串,將獲得每一個字符對應碼點的十六進制。但這不是字符串的UTF8編碼!spa

要拿到UTF8編碼,須要在碼點的基礎上多走一步。先回顧unicode碼點與UTF8的轉換關係:code

unicode碼點(十六進制) UTF-8(二進制)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

根據這個表,結合charCodeAt函數,能夠寫出字符串到UTF8編碼的函數:對象

// 獲取字符串的utf8字節流
function getUTF8Bytes(str) {
  var bytes = [];
  var len = str.length;
  for (var i = 0; i < len; ++ i) {
    var code = str.charCodeAt(i);
    if (code >= 0x10000 && code < = 0x10ffff) {
      bytes.push((code >> 18) | 0xf0);                // 第一個字節
      bytes.push(((code >> 12) & 0x3f) | 0x80);
      bytes.push(((code >> 6) & 0x3f) | 0x80);
      bytes.push((code & 0x3f) | 0x80);
    } else if (code >= 0x800 && code < = 0xffff) {
      bytes.push((code >> 12) | 0xe0);
      bytes.push(((code >> 6) & 0x3f) | 0x80);
      bytes.push((code & 0x3f) | 0x80);
    } else if (code >= 0x80 && code < = 0x7ff) {
       bytes.push((code >> 6) | 0xc0);
       bytes.push((code & 0x3f) | 0x80);
    } else {
      bytes.push(code)
    }
  }

  return bytes;
}

// 將字節流轉換成16進制字符串
function hexString(bytes) {
  var arr = bytes.map(function (code) {
    return (code).toString(16).toUpperCase();
  });

  return arr.join(' ');
}

function utf8(str) {
   return hexString(getUTF8Bytes(str));
}

調用函數utf8,這纔是JavaScript獲得字符串的UTF8編碼的正確姿式!從UTF8編碼轉換到字符串,作相反工做便可。ip

相關文章
相關標籤/搜索