JavaScript 常用編碼、解碼
24 Jul 2020常用的 UTF-8 之 encodeURI 與 decodeURI、encodeURIComponent 與 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…
備註
- (1) 將文字轉換為十六進制的唯一代號表示,例如:U+XXXX,這樣的編碼稱為 Unicode。
- (2) 經過 Unicode 編碼後的文字必須轉換(mapping)為 0 與 1 才能讓電腦讀懂,mapping 的其中一種方法是 UTF encodings,當中 UTF-8 是較為常用的一種實作方式。
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」。
如下圖,搜尋頁的網址就會得到空白用加號(+
)分隔 https://find.ruten.com.tw/s/?q=Hello+Kitty
。
表單提交資料
如下,提交表單資料「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+ |