成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

記一次由BOM引起的bug

cc17 / 1168人閱讀

摘要:今天團(tuán)隊(duì)小伙伴給了我一個(gè)配置文件,可以用如下替代畢竟內(nèi)容不是重點(diǎn)考慮到這個(gè)并不需要常駐,就沒(méi)有用來(lái)引用,因?yàn)槟K的緩存機(jī)制,勢(shì)必會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題的發(fā)生,就采取了以下方式但是詭異的事情發(fā)生了,竟然報(bào)錯(cuò)了此時(shí)一臉懵逼,就用了的方式試了一下發(fā)現(xiàn)

bug

今天團(tuán)隊(duì)小伙伴給了我一個(gè)json配置文件,可以用如下替代(畢竟內(nèi)容不是重點(diǎn)):

{
    "text": "this is a example"
}

考慮到這個(gè)json并不需要常駐,就沒(méi)有用require來(lái)引用,因?yàn)?b>node模塊的緩存機(jī)制,勢(shì)必會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題的發(fā)生,就采取了以下方式:

fs.readFile(`${__dirname}/y.json`, "utf8", function(err, str) {
  if (err) {
    throw err;
  }
  try {
    const data = JSON.parse(str);
    // ...
  } catch(err) {
    throw err;
  }
});

但是詭異的事情發(fā)生了,JSON.parse竟然報(bào)錯(cuò)了???

Unexpected token ? in JSON at position 0

此時(shí)一臉懵逼,就用了require的方式試了一下發(fā)現(xiàn)一點(diǎn)問(wèn)題都沒(méi)有,考慮到了團(tuán)隊(duì)小伙伴使用的windows,就去問(wèn)了下他,得知這個(gè)jsonnotepad++寫(xiě)的,加上之前寫(xiě)php經(jīng)常遇到的BOM問(wèn)題,就猜測(cè)這個(gè)bug由BOM引起,將讀出來(lái)的str轉(zhuǎn)成Buffer來(lái)看果然開(kāi)頭是ef bb bf。下面先來(lái)看下今天說(shuō)的這個(gè)BOM到底是個(gè)什么東西:

BOM

字節(jié)順序標(biāo)記(英語(yǔ):byte-order mark,BOM)是位于碼點(diǎn)U+FEFF的統(tǒng)一碼字符的名稱(chēng)。當(dāng)以UTF-16或UTF-32來(lái)將UCS/統(tǒng)一碼字符所組成的字符串編碼時(shí),這個(gè)字符被用來(lái)標(biāo)示其字節(jié)序。它常被用來(lái)當(dāng)做標(biāo)示文件是以UTF-8、UTF-16或UTF-32編碼的記號(hào)。

說(shuō)白了就是存在于文本文件的開(kāi)頭,標(biāo)記出文件是依靠那種格式進(jìn)行編碼的,mac上應(yīng)該不存在,但是windowsnotepad++一般會(huì)帶有。大家也可以用python寫(xiě)一個(gè)帶有BOM標(biāo)記的文件,來(lái)驗(yàn)證這個(gè)問(wèn)題:

import codecs

code = """{
    "x": 20
}
"""

f = codecs.open("y.json", "w", "utf_8_sig")
f.write(code)
f.close()

了解了產(chǎn)生原因以及BOM到底是什么,還有一個(gè)疑惑就是為什么用require引入可以?

require json做了啥

記得require是用的fs.readFileSync同步讀取的,為什么這個(gè)可以呢?猜測(cè)都是無(wú)用的,來(lái)看下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在讀取之后,對(duì)字符串進(jìn)行了去除BOM的操作,來(lái)看下internalModule.stripBOM的實(shí)現(xiàn):

function stripBOM(content) {
  // 檢測(cè)第一個(gè)字符是否為BOM
  if (content.charCodeAt(0) === 0xFEFF) {
    content = content.slice(1);
  }
  return content;
}

至此問(wèn)題已經(jīng)解決了,但是我還有一點(diǎn)不明白的是ef bb bfutf8的標(biāo)記,為什么會(huì)轉(zhuǎn)換為feff,這個(gè)不是utf16大端序的表示嗎?下面就來(lái)解決這個(gè)疑惑:

Unicode與utf8

先來(lái)講一下編碼的歷史,首先出現(xiàn)的表示字符編碼為ASCII,八位二進(jìn)制,可以表示出256種狀態(tài),英文用128個(gè)符號(hào)編碼就可以了,但是其他的語(yǔ)言卻無(wú)法表示,于是在一些歐洲國(guó)家,開(kāi)始各自規(guī)定其表示,比如130在法語(yǔ)代表一個(gè)字符,俄語(yǔ)代表一個(gè)字符,這樣造成了0-127一致,而128-255可能會(huì)千差萬(wàn)別;為了解決這種問(wèn)題,國(guó)際組織設(shè)計(jì)提出了Unicode,一個(gè)可以容納全世界所有語(yǔ)言文字的編碼方案,Unicode只規(guī)定了符號(hào)的二進(jìn)制代碼,但是沒(méi)有規(guī)定該如何存儲(chǔ),比如中文可能至少需要2個(gè)字節(jié),而英文只需要一個(gè)字節(jié)即可。utf8作為一種Unicode的實(shí)現(xiàn)方式被廣泛顎用于互聯(lián)網(wǎng)應(yīng)用中,utf8明確了編碼規(guī)則:

對(duì)于單字節(jié)的符號(hào),將其第一位置為0,使用后面7位進(jìn)行表示,所以說(shuō)英文utf8編碼與ASCII碼一致

對(duì)于n(n > 2)個(gè)字節(jié)的符號(hào),第一個(gè)字節(jié)的前n為都設(shè)置為1,第n+1為設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10,剩下的二進(jìn)制位,為這個(gè)符號(hào)的Unicode

可以參見(jiàn)以下對(duì)照:

字符字節(jié) Unicode符號(hào)范圍 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

來(lái)看下feff轉(zhuǎn)化為ef bb bf,fs.readFileSync進(jìn)行了buffer -> string的轉(zhuǎn)換,buffer的編碼為utf8,而stringUnicode,根據(jù)上表計(jì)算下:

F E F F
1111 1110 1111 1111

根據(jù)其范圍,得出其utf8編碼:

1110 1111 1011 1011 1011 1111
E F B B B F

用代碼來(lái)實(shí)現(xiàn)下Unicode轉(zhuǎn)utf8的過(guò)程:

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轉(zhuǎn)Unicode只需要去除標(biāo)志位即可,這里就不在實(shí)現(xiàn)。

到此,終于清楚的可以和團(tuán)隊(duì)小伙伴說(shuō)出bug的解決方法就利用上面的stripBOM

致謝

如有錯(cuò)誤,還請(qǐng)指出!

Unicode與utf8 部分內(nèi)容參考自阮老師文章

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/89907.html

相關(guān)文章

  • 一次線(xiàn)上bug處理-mybatis一級(jí)緩存引起

    摘要:?jiǎn)栴}線(xiàn)上定時(shí)任務(wù)計(jì)算出的金額不對(duì)定位問(wèn)題查看日志好像也執(zhí)行了但是金額為什么和數(shù)據(jù)庫(kù)的表里的不一致再查整個(gè)的定時(shí)任務(wù)日志日切日期 問(wèn)題: 線(xiàn)上riskProvision定時(shí)任務(wù),計(jì)算出的金額不對(duì) 定位問(wèn)題: 查看日志 4.13 riskProvision 2017-04-13 01:10:00.009 [org.springframework.scheduling.quartz....

    sean 評(píng)論0 收藏0
  • 一次低級(jí)并嚴(yán)重開(kāi)發(fā)失誤

    摘要:而這一次的項(xiàng)目,原本以為開(kāi)發(fā)挺順利的,但是開(kāi)發(fā)完了,才發(fā)現(xiàn)自己犯了一個(gè)低級(jí)而嚴(yán)重的錯(cuò),這樣的一個(gè)失誤,我一直耿耿于懷。但是監(jiān)聽(tīng)用戶(hù)退出頁(yè)面微信瀏覽器上面的那個(gè)返回或者關(guān)閉按鈕卻死活不行。也容易犯一些低級(jí)的錯(cuò)誤。 1.前言 前端從事了超過(guò)兩年,修復(fù)了無(wú)數(shù)的bug,寫(xiě)了無(wú)數(shù)的bug;挖了很多次坑,填了很多次坑;犯了很多次錯(cuò),彌補(bǔ)了很多次,學(xué)習(xí)了很多次。一般而言,對(duì)于bug、坑,都是修復(fù)完了...

    wudengzan 評(píng)論0 收藏0
  • 一次繪圖框架技術(shù)選型: jsPlumb VS mxGraph

    摘要:公司項(xiàng)目需要用到繪圖框架,繪圖部分以前是另一位同事負(fù)責(zé),用的是框架?;谝陨咸峒暗降姆N種原因,上年年末我做起了技術(shù)調(diào)研,希望能找到一個(gè)合適我們項(xiàng)目的繪圖框架。兼容性問(wèn)題項(xiàng)目對(duì)瀏覽器兼容性比較寬松,瀏覽器兼容性問(wèn)題不在考慮范圍之內(nèi)。 showImg(https://ws3.sinaimg.cn/large/006tKfTcgy1g0ppk2kkhxj30ka0b4gm5.jpg); 公司...

    longmon 評(píng)論0 收藏0
  • 一次繪圖框架技術(shù)選型: jsPlumb VS mxGraph

    摘要:公司項(xiàng)目需要用到繪圖框架,繪圖部分以前是另一位同事負(fù)責(zé),用的是框架?;谝陨咸峒暗降姆N種原因,上年年末我做起了技術(shù)調(diào)研,希望能找到一個(gè)合適我們項(xiàng)目的繪圖框架。兼容性問(wèn)題項(xiàng)目對(duì)瀏覽器兼容性比較寬松,瀏覽器兼容性問(wèn)題不在考慮范圍之內(nèi)。 showImg(https://ws3.sinaimg.cn/large/006tKfTcgy1g0ppk2kkhxj30ka0b4gm5.jpg); 公司...

    channg 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<