JS中unicode和utf-8的轉換
最近公司找了幾個py寫后端項目,后端接口中返回 '\xe6\x88\x91\xe4\xbb\xac' 類似的編碼,我看着就很好奇,於是將此段編碼過的字符輸入chrome的控制台,結果如下:
> '\xe6\x88\x91\xe4\xbb\xac'
< "æä»¬"
很明顯,由於解釋錯誤出現了亂碼問題。。。
在網上一番搜索發現,這就是utf-8編碼,本着好奇,就想知道unicode和utf-8之間是如何轉換的。。。至於utf-8和unicode的區別,我只強調一點 UTF-8是 Unicode 的實現方式之一 ,具體的話大家網上自行查找,這里提供我參考的文章 字符編碼筆記:ASCII,Unicode 和 UTF-8,下面我總結下在js中這兩種編碼之間如何轉換
unicode轉utf-8
我們知道在js中,encodeURI和encodeURIComponent函數將URI轉為utf-8編碼:
> encodeURIComponent('深圳華強')
< "%E6%B7%B1%E5%9C%B3%E5%8D%8E%E5%BC%BA"
在網上驗證下沒問題:
/**
*
* @param str {String}
* @return {Array{Number}}
*/
function encodeUTF8 (str = '深圳華強') {
let str1 = encodeURIComponent(str) // "%E6%B7%B1%E5%9C%B3%E5%8D%8E%E5%BC%BA"
let ret = []
for (let i = 0; i < str1.length / 3; i++) {
ret.push(str1.slice(i * 3, (i + 1) * 3).slice(1))
}
// ret = ["E6", "B7", "B1", "E5", "9C", "B3", "E5", "8D", "8E", "E5", "BC", "BA"]
return ret.map(el => parseInt(el, 16)) // [230, 183, 177, 229, 156, 179, 229, 141, 142, 229, 188, 186]
}
utf-8轉unicode
/**
*
* @param arr {Array{Number}}
* @return {string}
*/
function decodeUTF8 (arr = [230, 183, 177, 229, 156, 179, 229, 141, 142, 229, 188, 186]) {
let str = arr.reduce((prev, cur) => prev +=`%${cur.toString(16)}`, '')
return decodeURIComponent(str) // '深圳華強'
}
測試
> encodeUTF8()
< [230, 183, 177, 229, 156, 179, 229, 141, 142, 229, 188, 186]
> decodeUTF8()
< "深圳華強"
最后回到我們開頭的問題, '\xe6\x88\x91\xe4\xbb\xac' 到底代表什么意思?
我嘗試很很多種方法,發現只要js識別到 '\xe6\x88\x91\xe4\xbb\xac' 馬上就進行解碼了,根本沒有機會操作。。。最后我發現將其中\要先轉義處理:'\xe6\x88\x91\xe4\xbb\xac',然后就好處理了,如果這個東西要前端要展示的話,只能暫時求助后端同學提前對反斜杠進行轉移處理了。。。
let reg = /\\x/g
console.log('\\xe6\\x88\\x91\\xe4\\xbb\\xac') // \xe6\x88\x91\xe4\xbb\xac
console.log('\\xe6\\x88\\x91\\xe4\\xbb\\xac'.replace(reg, '%')) // %e6%88%91%e4%bb%ac
console.log(decodeURIComponent('\\xe6\\x88\\x91\\xe4\\xbb\\xac'.replace(reg, '%'))) // 我們
續更(2020.7.21)
對於昨天結尾遺留的問題,我找到了不完美的解決方案:ECMAScript 6 入門 String.raw()
ES6 還為原生的 String 對象,提供了一個raw()方法。該方法返回一個斜杠都被轉義(即斜杠前面再加一個斜杠)的字符串,往往用於模板字符串的處理方法
String.raw`Hi\n${2+3}!`
// 實際返回 "Hi\\n5!",顯示的是轉義后的結果 "Hi\n5!"
String.raw`Hi\u000A!`;
// 實際返回 "Hi\\u000A!",顯示的是轉義后的結果 "Hi\u000A!"
解決方案跟昨天后端童鞋解決方案一樣,通過 String.raw 對反斜杠\進行轉義,如下:
let reg = /\\x/g
let str = String.raw`\xe6\x88\x91\xe4\xbb\xac`
// 實際返回 "\\xe6\\x88\\x91\\xe4\\xbb\\xac",顯示的是轉義后的記過 "\xe6\x88\x91\xe4\xbb\xac"
// 后續的操作就跟上邊一樣了,String.raw缺陷就是沒辦法傳入變量...
let str = '\xe6\x88\x91\xe4\xbb\xac'
String.raw`${str }`
// 返回 "æä»¬" 相當於還是解析str了,導致String.raw失效了
參考資料:
UTF-8編碼規則(轉)
rfc3629
查看字符編碼(UTF-8)在線工具
字符編碼的前世今生
兩次encodeURI和URLDecode的原理分析
JavaScript進行UTF-8編碼與解碼