摘要:今天團隊小伙伴給了我一個配置文件,可以用如下替代畢竟內容不是重點考慮到這個并不需要常駐,就沒有用來引用,因為模塊的緩存機制,勢必會導致內存泄漏問題的發生,就采取了以下方式但是詭異的事情發生了,竟然報錯了此時一臉懵逼,就用了的方式試了一下發現
bug
今天團隊小伙伴給了我一個json配置文件,可以用如下替代(畢竟內容不是重點):
{ "text": "this is a example" }
考慮到這個json并不需要常駐,就沒有用require來引用,因為node模塊的緩存機制,勢必會導致內存泄漏問題的發生,就采取了以下方式:
fs.readFile(`${__dirname}/y.json`, "utf8", function(err, str) { if (err) { throw err; } try { const data = JSON.parse(str); // ... } catch(err) { throw err; } });
但是詭異的事情發生了,JSON.parse竟然報錯了???
Unexpected token ? in JSON at position 0
此時一臉懵逼,就用了require的方式試了一下發現一點問題都沒有,考慮到了團隊小伙伴使用的windows,就去問了下他,得知這個json用notepad++寫的,加上之前寫php經常遇到的BOM問題,就猜測這個bug由BOM引起,將讀出來的str轉成Buffer來看果然開頭是ef bb bf。下面先來看下今天說的這個BOM到底是個什么東西:
BOM字節順序標記(英語:byte-order mark,BOM)是位于碼點U+FEFF的統一碼字符的名稱。當以UTF-16或UTF-32來將UCS/統一碼字符所組成的字符串編碼時,這個字符被用來標示其字節序。它常被用來當做標示文件是以UTF-8、UTF-16或UTF-32編碼的記號。
說白了就是存在于文本文件的開頭,標記出文件是依靠那種格式進行編碼的,mac上應該不存在,但是windows的notepad++一般會帶有。大家也可以用python寫一個帶有BOM標記的文件,來驗證這個問題:
import codecs code = """{ "x": 20 } """ f = codecs.open("y.json", "w", "utf_8_sig") f.write(code) f.close()
了解了產生原因以及BOM到底是什么,還有一個疑惑就是為什么用require引入可以?
require json做了啥記得require是用的fs.readFileSync同步讀取的,為什么這個可以呢?猜測都是無用的,來看下node的源碼,找到了這段:
Module._extensions[".json"] = function(module, filename) { var content = fs.readFileSync(filename, "utf8"); try { module.exports = JSON.parse(internalModule.stripBOM(content)); } catch (err) { err.message = filename + ": " + err.message; throw err; } };
看了上面的代碼可以非常明了,require在讀取之后,對字符串進行了去除BOM的操作,來看下internalModule.stripBOM的實現:
function stripBOM(content) { // 檢測第一個字符是否為BOM if (content.charCodeAt(0) === 0xFEFF) { content = content.slice(1); } return content; }
至此問題已經解決了,但是我還有一點不明白的是ef bb bf為utf8的標記,為什么會轉換為feff,這個不是utf16大端序的表示嗎?下面就來解決這個疑惑:
Unicode與utf8先來講一下編碼的歷史,首先出現的表示字符編碼為ASCII,八位二進制,可以表示出256種狀態,英文用128個符號編碼就可以了,但是其他的語言卻無法表示,于是在一些歐洲國家,開始各自規定其表示,比如130在法語代表一個字符,俄語代表一個字符,這樣造成了0-127一致,而128-255可能會千差萬別;為了解決這種問題,國際組織設計提出了Unicode,一個可以容納全世界所有語言文字的編碼方案,Unicode只規定了符號的二進制代碼,但是沒有規定該如何存儲,比如中文可能至少需要2個字節,而英文只需要一個字節即可。utf8作為一種Unicode的實現方式被廣泛顎用于互聯網應用中,utf8明確了編碼規則:
對于單字節的符號,將其第一位置為0,使用后面7位進行表示,所以說英文utf8編碼與ASCII碼一致
對于n(n > 2)個字節的符號,第一個字節的前n為都設置為1,第n+1為設為0,后面字節的前兩位一律設為10,剩下的二進制位,為這個符號的Unicode碼
可以參見以下對照:
字符字節 | Unicode符號范圍 | utf8編碼方式 |
---|---|---|
1 | 0000 0000 - 0000 007F | 0xxxxxxx |
2 | 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx |
3 | 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
4 | 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
5 | 0020 0000 - 03FF FFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx |
來看下feff轉化為ef bb bf,fs.readFileSync進行了buffer -> string的轉換,buffer的編碼為utf8,而string為Unicode,根據上表計算下:
F | E | F | F |
---|---|---|---|
1111 | 1110 | 1111 | 1111 |
根據其范圍,得出其utf8編碼:
1110 | 1111 | 1011 | 1011 | 1011 | 1111 |
---|---|---|---|---|---|
E | F | B | B | B | F |
用代碼來實現下Unicode轉utf8的過程:
def UnicodeToUtf8(unic): res = list() if unic < 0x7F: res.append(hex(unic & 0x7F)) elif unic >= 0x80 and unic <= 0x7FF: # 110xxxxx res.append(((unic >> 6) & 0x1F) | 0xC0) # 10xxxxxx res.append((unic & 0x3F) | 0x80) elif unic >= 0x800 and unic <= 0xFFFF: # 1110xxxx res.append(((unic >> 12) & 0x0F) | 0xE0) # all is 10xxxxxx res.append(((unic >> 6) & 0x3F) | 0x80) res.append((unic & 0x3F) | 0x80) elif unic >= 0x10000 and unic <= 0x1FFFFF: # 11110xxx res.append(((unic >> 18) & 0x07) | 0xF0) # all is 10xxxxxx res.append(((unic >> 12) & 0x3F) | 0x80) res.append(((unic >> 6) & 0x3F) | 0x80) res.append((unic & 0x3F) | 0x80) elif unic >= 0x200000 and unic <= 0x3FFFFFF: # 111110xx res.append(((unic >> 24) & 0x03) | 0xF8) # all is 10xxxxxx res.append(((unic >> 18) & 0x3F) | 0x80) res.append(((unic >> 12) & 0x3F) | 0x80) res.append(((unic >> 6) & 0x3F) | 0x80) res.append((unic & 0x3F) | 0x80) elif unic >= 0x4000000 and unic <= 0x7FFFFFFF: # 1111110x res.append(((unic >> 30) & 0x01) | 0xFC) # all is 10xxxxxx res.append(((unic >> 24) & 0x3F) | 0x80) res.append(((unic >> 18) & 0x3F) | 0x80) res.append(((unic >> 12) & 0x3F) | 0x80) res.append(((unic >> 6) & 0x3F) | 0x80) res.append((unic & 0x3F) | 0x80) return map(lambda r:hex(r), res) # test print UnicodeToUtf8(0xFEFF)
utf8轉Unicode只需要去除標志位即可,這里就不在實現。
到此,終于清楚的可以和團隊小伙伴說出bug的解決方法就利用上面的stripBOM
致謝如有錯誤,還請指出!
Unicode與utf8 部分內容參考自阮老師文章
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89907.html
摘要:問題線上定時任務計算出的金額不對定位問題查看日志好像也執行了但是金額為什么和數據庫的表里的不一致再查整個的定時任務日志日切日期 問題: 線上riskProvision定時任務,計算出的金額不對 定位問題: 查看日志 4.13 riskProvision 2017-04-13 01:10:00.009 [org.springframework.scheduling.quartz....
摘要:而這一次的項目,原本以為開發挺順利的,但是開發完了,才發現自己犯了一個低級而嚴重的錯,這樣的一個失誤,我一直耿耿于懷。但是監聽用戶退出頁面微信瀏覽器上面的那個返回或者關閉按鈕卻死活不行。也容易犯一些低級的錯誤。 1.前言 前端從事了超過兩年,修復了無數的bug,寫了無數的bug;挖了很多次坑,填了很多次坑;犯了很多次錯,彌補了很多次,學習了很多次。一般而言,對于bug、坑,都是修復完了...
摘要:公司項目需要用到繪圖框架,繪圖部分以前是另一位同事負責,用的是框架。基于以上提及到的種種原因,上年年末我做起了技術調研,希望能找到一個合適我們項目的繪圖框架。兼容性問題項目對瀏覽器兼容性比較寬松,瀏覽器兼容性問題不在考慮范圍之內。 showImg(https://ws3.sinaimg.cn/large/006tKfTcgy1g0ppk2kkhxj30ka0b4gm5.jpg); 公司...
摘要:公司項目需要用到繪圖框架,繪圖部分以前是另一位同事負責,用的是框架。基于以上提及到的種種原因,上年年末我做起了技術調研,希望能找到一個合適我們項目的繪圖框架。兼容性問題項目對瀏覽器兼容性比較寬松,瀏覽器兼容性問題不在考慮范圍之內。 showImg(https://ws3.sinaimg.cn/large/006tKfTcgy1g0ppk2kkhxj30ka0b4gm5.jpg); 公司...
閱讀 984·2021-11-24 09:39
閱讀 2184·2021-11-16 11:54
閱讀 2077·2021-11-11 17:22
閱讀 2372·2021-09-30 09:55
閱讀 3590·2021-08-12 13:22
閱讀 1625·2019-08-30 15:44
閱讀 1168·2019-08-29 12:12
閱讀 3262·2019-08-27 10:58