JavaScript 常用編碼、解碼

常用的 UTF-8 之 encodeURI 與 decodeURIencodeURIComponent 與 decodeURIComponent 和 Base64 之 btoa 與 atob

UTF-8 Encode 與 Decode

電腦世界最初的編碼是使用 ASCII 編碼,由於 ASCII 編碼只能表達 128 種英文與數字(2^7 = 128),故無法完整表示符號等,雖然後來有 EASCII(Extended ASCII,擴充的 ASCII),但終究還是不敷使用。在經歷眾多編碼規則發展,資訊交流迫使需要有統一的編碼規則來適應多國語系,像是非英語系的文字 - 中文、阿拉伯文等,因此有了萬國碼(Unicode),而常用的 UTF-8 為 Unicode 編碼家族的其中一支,UTF-8 是可變長度的編碼而不浪費空間,並可向下相容 ASCII。

在 JavaScript 的世界裡,主要使用兩種方式來做 UTF-8 編碼與解碼

Unicode 與 UTF-8 的關係

Text (A, B, C,…) -> encoded by Unicode (備註 1) -> Text (U+XXXX…) -> mapped by UTF-8 (備註 2) -> 0, 1…

備註

對編碼相關有興趣可參考這裡那裡

encodeURI 與 decodeURI

如下範例,使用 encodeURI 與 decodeURI 將中文字串做 UTF-8 編碼和對編碼後的結果做解碼。

const encodedStr = encodeURI('這是中文字串'); // encodedStr 得到 %E9%80%99%E6%98%AF%E4%B8%AD%E6%96%87%E5%AD%97%E4%B8%B2
const decodedStr = decodeURI(encodedStr); // decodedStr 得到 這是中文字串

與下面即將提到的 encodeURIComponent 與 decodeURIComponent 不同之處,在於 encodeURI 與 decodeURI 不會將這些具功能性的保留字符一起編碼(例如::/;?=&。),不會導致網址失效。反過來說,若 client 端將這些字串送到 server 端,則可能會因為被 server 誤判為特殊指令而中斷剖析。

encodeURI('&'); // "&"
decodeURI('&'); // "&"

encodeURI('&') === decodeURI('&'); // true

encodeURIComponent 與 decodeURIComponent

使用 encodeURIComponent 與 decodeURIComponent 將中文字串做 UTF-8 編碼和對編碼後的結果做解碼。

const encodedStr = encodeURIComponent('這是中文字串'); // encodedStr 得到 %E9%80%99%E6%98%AF%E4%B8%AD%E6%96%87%E5%AD%97%E4%B8%B2
const decodedStr = decodeURIComponent(encodedStr); // decodedStr 得到 這是中文字串

與上面提過的 encodeURI 與 decodeURI 不同之處,在於 encodeURIComponent 與 decodeURIComponent 會將這些具功能性的保留字符一起編碼,也就是說會導致網址失效,因此只能對網址做局部編碼。

encodeURIComponent('&'); // "%26"
decodeURIComponent('&'); // "&"

encodeURIComponent('&') === decodeURIComponent('&'); // false

encodeURIComponent 與 decodeURIComponent 主要是用於網址參數的編碼,像是 https://www.sample.com?id=https://www.helloworld.com 就需要將 id 後的 https://www.helloworld.com 做 encodeURIComponent 而得到 https://www.sample.com?id=https%3A%2F%2Fwww.helloworld.com,需要做這件事是因為某些舊瀏覽器只能閱讀被編碼後的 query string 的,也就是說,對於這些老舊瀏覽器來說,不是合法的 URI 是看不懂的 XD


注意,在 client 端送資料給 server 端時,若使用 query string 的方式,格式通常是這樣 https://www.sample.com?foo=abc&bar=def,而若當中有空白的話,像是 foo=a b c a、b、c 中間有空白,則會有以下加號(+)和 %20 兩種情況(可參考這裡)。

PHP 的 urlencode 與 rawurlencode

PHP 的 urlencode 會將空白編碼為加號(+),例如:https://www.sample.com?foo=a+b+c&bar=def,但若用 rawurlencode 就會將空白編碼為 %20,例如:https://www.sample.com?foo=a%20b%20c&bar=def

如下圖,在露天拍賣搜尋「Hello Kitty」。

PHP urlencode

如下圖,搜尋頁的網址就會得到空白用加號(+)分隔 https://find.ruten.com.tw/s/?q=Hello+Kitty

PHP urlencode

表單提交資料

如下,提交表單資料「a b c」,表單的資料傳送預設是 content-type 為 application/x-www-form-urlencoded 會將空白編碼為 +,因此會得到 request URL 為 https://sample.com?message=a+b+c,關於 content-type 可參考這裡

<form action="/sample.com" method="GET">
  <input name="message" value="a b c">
  <button typ="submit">submit</button>
</form>

順道一提,若提交表單資料為「a&b&c」,則會將 &URL 編碼(URL encoding,或稱百分號編碼 percent encoding)為 a%26b%26c

<form action="/sample.com" method="GET">
  <input name="message" value="a&b&c">
  <button typ="submit">submit</button>
</form>

JavaScript 的 URI 編碼

JavaScript 的 encodeURI 與 encodeURIComponent 是將空白編碼為 %20

Base64 Encode 與 Decode

電腦間的通訊除了使用 0 和 1 溝通外,還會希望能傳送文字或圖檔等更豐富媒體,而這些媒體若要被傳送,就還是必須先編碼為 0 和 1,對方收到之後再解碼來做還原成原始資訊。最初使用 ASCII 來做編碼,但由於 ASCII 的每個字元是由 7 個 bit 組成,而大多數的電腦是使用 8 個 bit 來存一個字元的,因此 ASCII 並不適合這樣的編解碼傳輸工作(當然還有其他問題)。因此 Base64 應運而生,Base64 可對任何位元組做編碼以將資料做傳送,缺點是它有點肥(因為每 3 個 byte 會編碼為 4 個 ASCII 字元)。在實際應用上。我們會先將資料做 UTF-8 編碼為位元組,再做 Base64 編碼將位元組轉為字串,有興趣的可以參考這裡

在 JavaScript 的世界裡,可用 btoa()(binary to ASCII)和 atob()(ASCII to binary)來做 Base64 的編碼和解碼。主要是用於 Data URIs

如下範例,對字串 HelloWorld 做 Base64 的編碼與解碼。

const encodedData = btoa('HelloWorld'); // encodedData 得到 "SGVsbG9Xb3JsZA=="
const decodedData = atob(encodedData); // decodedData 得到 "HelloWorld"

如前面所說,由於 ASCII 無法表示中文,因此要先做 UTF-8 編碼,然後再做 Base64 編碼;解碼方式為先做 Base64 解碼,再做 UTF-8 解碼。

const encodedData = btoa(encodeURI('你好')); // encodedData 得到 "JUU0JUJEJUEwJUU1JUE1JUJE"
const decodedData = decodeURI(atob(encodedData)); // decodedData 得到 "你好"

注意,btoa()atob() 瀏覽器支援度,像是 IE 10 以後才有支援,可分別參考這裡那裡

總結

表格整理如下。

項目 encodeURI 與 decodeURI encodeURIComponent 與 decodeURIComponent btoa 與 atob
用途 轉換 URL,避免剖析失敗 轉換 URL,避免剖析失敗 資料傳輸時防止被更改、DATA URI
編碼 UTF-8 UTF-8 ASCII
對於保留字符的編碼(例如:對於「&」的編碼?) 不做編碼(& 會編碼(%26 會編碼(Jg==
IE 支援度 IE 5.5+ IE 5.5+ IE 10+

comments powered by Disqus