<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                作為前端的JSer,是一件非常幸福的事情,因為在字符串上從來沒有出現過任何糾結的問題。我們來看看PHP對字符串長度的判斷結果: ~~~ <?php echo strlen("0123456789"); echo strlen("零一二三四五六七八九"); echo mb_strlen("零一二三四五六七八九", "utf-8"); echo "\n"; ~~~ 以上三行判斷分別返回10、30、10。對于中國人而言,strlen這個方法對于Unicode的判斷結果是非常讓人疑惑。而看看JavaScript中對字符串長度的判斷,就知道這個length屬性對調用者而言是多么友好。 ~~~ console.log("0123456789".length); // 10 console.log("零一二三四五六七八九".length); /10 console.log("\u00bd".length); // 1 ~~~ 盡管在計算機內部,一個中文字和一個英文字占用的字節位數是不同的,但對于用戶而言,它們擁有相同的長度。我認為這是JavaScript中 String處理得精彩的一個點。正是由于這個原因,所有的數據從后端傳輸到前端被調用時,都是這般友好的字符串。所以對于前端工程師而言,他們是沒有字 符串Buffer的概念的。如果你是一名前端工程師,那么從此在與Node.js打交道的過程中,一定要小心Buffer啦,因為它比傳統的String 要調皮一點。 ## 你該小心Buffer啦 像許多計算機的技術一樣,都是從國外傳播過來的。那些以英文作為母語的傳道者們應該沒有考慮過英文以外的使用者,所以你有可能看到如下這樣一段代碼在向你描述如何在data事件中連接字符串。 ~~~ var fs = require('fs'); var rs = fs.createReadStream('testdata.md'); var data = ''; rs.on("data", function (trunk){ data += trunk; }); rs.on("end", function () { console.log(data); }); ~~~ 如果這個文件讀取流讀取的是一個純英文的文件,這段代碼是能夠正常輸出的。但是如果我們再改變一下條件,將每次讀取的buffer大小變成一個奇數,以模擬一個字符被分配在兩個trunk中的場景。 ~~~ var rs = fs.createReadStream('testdata.md', {bufferSize: 11}); ~~~ 我們將會得到以下這樣的亂碼輸出: ~~~ 事件循???和請求???象構成了Node.js???異步I/O模型的???個基本???素,這也是典???的消費???生產者場景。 ~~~ 造成這個問題的根源在于data += trunk語句里隱藏的錯誤,在默認的情況下,trunk是一個Buffer對象。這句話的實質是隱藏了toString的變換的: ~~~ data = data.toString() + trunk.toString(); ~~~ 由于漢字不是用一個字節來存儲的,導致有被截破的漢字的存在,于是出現亂碼。解決這個問題有一個簡單的方案,是設置編碼集: ~~~ var rs = fs.createReadStream('testdata.md', {encoding: 'utf-8', bufferSize: 11}); ~~~ 這將得到一個正常的字符串響應: ~~~ 事件循環和請求對象構成了Node.js的異步I/O模型的兩個基本元素,這也是典型的消費者生產者場景。 ~~~ 遺憾的是目前Node.js僅支持hex、utf8、ascii、binary、base64、ucs2幾種編碼的轉換。對于那些因為歷史遺留問題依舊還生存著的GBK,GB2312等編碼,該方法是無能為力的。 ## 有趣的string_decoder 在這個例子中,如果仔細觀察,會發現一件有趣的事情發生在設置編碼集之后。我們提到data += trunk等價于data = data.toString() + trunk.toString()。通過以下的代碼可以測試到一個漢字占用三個字節,而我們按11個字節來截取trunk的話,依舊會存在一個漢字被分割在兩個trunk中的情景。 ~~~ console.log("事件循環和請求對象".length); console.log(new Buffer("事件循環和請求對象").length); ~~~ 按照猜想的toString()方式,應該返回的是事件循xxx和請求xxx象才對,其中“環”字應該變成亂碼才對,但是在設置了encoding(默認的utf8)之后,結果卻正常顯示了,這個結果十分有趣。 ![](https://box.kancloud.cn/2015-07-16_55a7213db4659.png) 在好奇心的驅使下可以探查到[data事件](https://github.com/joyent/node/blob/master/lib/fs.js#L1237)調用了string_decoder來進行編碼補足的行為。通過string_decoder對象輸出第一個截取Buffer(事件循xx)時,只返回事件循這個字符串,保留xx。第二次通過string_decoder對象輸出時檢測到上次保留的xx,將上次剩余內容和本次的Buffer進行重新拼接輸出。于是達到正常輸出的目的。 string_decoder,目前在文件流讀取和網絡流讀取中都有應用到,一定程度上避免了粗魯拼接trunk導致的亂碼錯誤。但是,遺憾在于string_decoder目前只支持utf8編碼。它的思路其實還可以擴展到其他編碼上,只是最終是否會支持目前尚不可得知。 ## 連接Buffer對象的正確方法 那么萬能的適應各種編碼而且正確的拼接Buffer對象的方法是什么呢?我們從Node.js在github上的源碼中找出這樣一段[正確讀取文件,并連接buffer對象的方法](https://github.com/joyent/node/blob/master/lib/fs.js#L107): ~~~ var buffers = []; var nread = 0; readStream.on('data', function (chunk) { buffers.push(chunk); nread += chunk.length; }); readStream.on('end', function () { var buffer = null; switch(buffers.length) { case 0: buffer = new Buffer(0); break; case 1: buffer = buffers[0]; break; default: buffer = new Buffer(nread); for (var i = 0, pos = 0, l = buffers.length; i < l; i++) { var chunk = buffers[i]; chunk.copy(buffer, pos); pos += chunk.length; } break; } }); ~~~ 在end事件中通過細膩的連接方式,最后拿到理想的Buffer對象。這時候無論是在支持的編碼之間轉換,還是在不支持的編碼之間轉換(利用iconv模塊轉換),都不會導致亂碼。 ## 簡化連接Buffer對象的過程 上述一大段代碼僅只完成了一件事情,就是連接多個Buffer對象,而這種場景需求將會在多個地方發生,所以,采用一種更優雅的方式來完成該過程是必要的。筆者基于以上的代碼封裝出一個bufferhelper模塊,用于更簡潔地處理Buffer對象。可以通過NPM進行安裝: ~~~ npm install bufferhelper ~~~ 下面的例子演示了如何調用這個模塊。與傳統data += trunk之間只是bufferHelper.concat(chunk)的差別,既避免了錯誤的出現,又使得代碼可以得到簡化而有效地編寫。 ~~~ var http = require('http'); var BufferHelper = require('bufferhelper'); http.createServer(function (request, response) { var bufferHelper = new BufferHelper(); request.on("data", function (chunk) { bufferHelper.concat(chunk); }); request.on('end', function () { var html = bufferHelper.toBuffer().toString(); response.writeHead(200); response.end(html); }); }).listen(8001); ~~~ 所以關于Buffer對象的操作的最佳實踐是: * 保持編碼不變,以利于后續編碼轉換 * 使用封裝方法達到簡潔代碼的目的 ## 參考 * [https://github.com/joyent/node/blob/master/lib/fs.js#L107](https://github.com/joyent/node/blob/master/lib/fs.js#L107) * [https://github.com/JacksonTian/bufferhelper](https://github.com/JacksonTian/bufferhelper)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看