在上篇文章Buffer(Buffer(緩衝器))中,聊了關於編碼的問題。可是編碼有不少小坑,今天咱們聊聊坑的問題。 第一個就是BOM頭的問題。 咱們都知道,NodeJs是不支持gb2312編碼的, 在此以前得先知道,gb2312編碼中,一個漢字是由兩個字節(16個位)組成。 在咱們寫代碼的時候常常會遇到一個問題,就是咱們寫的代碼是gbk寫的(gb2312),但NodeJs是不支持的。因此讀取出來的數據,不是咱們想要的。javascript
let fs = require('fs');
let path = require('path');
let result = fs.readFileSync(path.join(__dirname,'./1.txt'));//txt的內容是前端開發
console.log(result.toString());
複製代碼
輸出的內容是亂碼 html
用編輯器打開txt文件也是亂碼 若是不對結果進行toString,獲得的buffer的內容是: 一般,咱們遇到不支持gbk的文件,第一反應都會從新設置編碼爲utf8格式。例如對txt的操做: 這時,再去獲取result的值let fs = require('fs');
let path = require('path');
let result = fs.readFileSync(path.join(__dirname,'./1.txt'));//txt的編碼已是utf8
console.log(result);
複製代碼
結果是 前端
咱們都知道uft8格式的文件,一個漢字3個字節,此時輸出的結果卻多出3個字節。由於這是unicode的緣由,它會加多3個字節的前綴。這個前綴對咱們來講是沒有意義的。對result進行toString()轉譯:console.log(result.toString())
複製代碼
輸出結果: java
這時咱們就要截掉這個BOM頭。咱們看看node源碼,編譯的時候用了stripBOM的模塊,把BOM頭刪掉node
// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(internalModule.stripBOM(content), filename);
};
複製代碼
咱們再看看源碼裏stripBOM的方法golang
/** * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) * because the buffer-to-string conversion in `fs.readFileSync()` * translates it to FEFF, the UTF-16 BOM. */
function stripBOM(content) {
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
return content;
}
複製代碼
stripBOM拿到內容content之後,取它的第0個,判斷它的第0個是否是0xFEFF,0xFEFF就是那3個前綴的字符,那3個字符是不要的,因此作了slice處理。 stripBOM方法裏要求content必須得是字符串,由於它截了一個,可是咱們的buffer是3個字節,因此咱們要對文件傳utf8的參數:segmentfault
let fs = require('fs');
let path = require('path');
let result = fs.readFileSync(path.join(__dirname,'./1.txt'),'utf8');
console.log(result);
複製代碼
此時result的結果就是一個字符串了: api
取出result的第一個字符等於0xFEFF的話,就要slice掉。let fs = require('fs');
let path = require('path');
function stripBOM(content) {
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
return content;
}
let result = fs.readFileSync(path.join(__dirname,'./1.txt'),'utf8');
result = stripBOM(result);
console.log(result);
//輸出:前端開發
複製代碼
通常狀況下,咱們讀取文件的時候不多會傳utf8這個參數,若是不傳utf8參數,該怎麼去掉BOM頭?(不傳utf8,獲得的就是buffer;傳了utf8,獲得的就是字符串)網絡
/* Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) 源碼已經說明,uft8中,EF BB BF表示3個字節,那麼只需判斷buffer的前3位是EF BB BF,就能夠刪掉 */
function stripBOM(content){
if(Buffer.isBuffer(content)){//判斷是否是buffer
if(content[0]===0xEF&&content[1]===0xBB&&content[2]===0xBF){
return content.slice(3);
}
return content;
}else{ //是string
if(content.charCodeAt(0)===0xFEFF){
return content.slice(1);
}
return content;
}
}
複製代碼
咱們用nodejs爬取gb2312網頁的時候,會出現亂碼的狀況。能夠用iconv-lite
把gbk轉化成utf8,它是第三方模塊,因此須要安裝包。這個包的目的就是幫助咱們轉化編碼。 如何調用:編輯器
let iconv = require('iconv-lite');
let fs = require('fs');
let path = require('path');
//iconv.decode(但願解碼的目標,但願按什麼方式解碼)
let result = fs.readFileSync(path.join(__dirname,'./2.txt'));
result = iconv.decode(result,'gbk')
console.log(result.toString())
複製代碼
因此,若是隻想要Buffer,咱們通常不傳編碼;若是想看這個結果是個字符串,咱們就傳utf8
string_decoder
模塊用於將Buffer轉成對應的字符串。使用者經過調用stringDecoder.write(buffer)
,能夠得到buffer對應的字符串。
它的特殊之處在於,當傳入的buffer不完整(好比三個字節的字符,只傳入了兩個),內部會維護一個internal buffer將不完整的字節cache住,等到使用者再次調用stringDecoder.write(buffer)
傳入剩餘的字節,來拼成完整的字符。
這樣能夠有效避免buffer不完整帶來的錯誤,對於不少場景,好比網絡請求中的包體解析等,很是有用。
這節分別演示了decode.write(buffer)、decode.end([buffer])兩個主要API的用法。
例子一:
decoder.write(buffer)調用傳入了Buffer對象,相應的返回了對應的字符串你;
const StringDecoder = require('string_decoder').StringDecoder;
const decoder = new StringDecoder('utf8');
// Buffer.from('你') => <Buffer e4 bd a0>
const str = decoder.write(Buffer.from([0xe4, 0xbd, 0xa0]));
console.log(str); // 你
複製代碼
例子二:
當decoder.end([buffer])被調用時,內部剩餘的buffer會被一次性返回。若是此時帶上buffer參數,那麼至關於同時調用decoder.write(buffer)和decoder.end()。
const StringDecoder = require('string_decoder').StringDecoder;
const decoder = new StringDecoder('utf8');
// Buffer.from('你好') => <Buffer e4 bd a0 e5 a5 bd>
let str = decoder.write(Buffer.from([0xe4, 0xbd, 0xa0, 0xe5, 0xa5]));
console.log(str); // 你
str = decoder.end(Buffer.from([0xbd]));
console.log(str); // 好
複製代碼
下面的例子,演示了分屢次寫入多個字節時,string_decoder模塊是怎麼處理的。
首先,傳入了,好還差1個字節,此時,decoder.write(xx)返回你。
而後,再次調用decoder.write(Buffer.from([0xbd])),將剩餘的1個字節傳入,成功返回好。
const StringDecoder = require('string_decoder').StringDecoder;
const decoder = new StringDecoder('utf8');
// Buffer.from('你好') => <Buffer e4 bd a0 e5 a5 bd>
let str = decoder.write(Buffer.from([0xe4, 0xbd, 0xa0, 0xe5, 0xa5]));
console.log(str); // 你
str = decoder.write(Buffer.from([0xbd]));
console.log(str); // 好
複製代碼
let buffer = Buffer.from('前端開發');
let buff1 = buffer.slice(0,5);
let buff2 = buffer.slice(5);
let {StringDecoder} = require('string_decoder');
let sd = new StringDecoder();
console.log(sd.write(buff1).toString());
console.log(sd.write(buff2).toString());
複製代碼
decoder.end(buffer)時,僅傳入了好的第1個字節,此時調用decoder.end(),返回了�,對應的buffer爲。
const StringDecoder = require('string_decoder').StringDecoder;
// Buffer.from('好') => <Buffer e5 a5 bd>
let decoder = new StringDecoder('utf8');
let str = decoder.end( Buffer.from([0xe5]) );
console.log(str); // �
console.log(Buffer.from(str)); // <Buffer ef bf bd>
複製代碼
參考文檔: