在上篇文章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
let fs = require('fs');
let path = require('path');
let result = fs.readFileSync(path.join(__dirname,'./1.txt'));//txt的编码已是utf8
console.log(result);
复制代码
结果是 前端
console.log(result.toString())
复制代码
输出结果: java
咱们看看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
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>
复制代码
参考文档: