深入浅出Node.js読書ノート:Bufferのつなぎ合わせ(6.3)

18009 ワード

Bufferの使用では、通常、入力ストリームでコンテンツを読み出すコードとして、一定の転送が行われます.
	var fs = require('fs'); 
	var rs = fs.createReadStream('test.md'); 
	var data = ''; 
	rs.on("data", function (chunk){
      
	 data += chunk; 
	}); 
	rs.on("end", function () {
      
	 console.log(data); 
	});

上記のコードは読み出しストリームのモデルコードであり,初心者にとっては問題はない.しかし、よく考えてみると、ストリームにワイドバイト符号化がある場合、問題が発生します.問題はdata+=chunkです.このコードには、次のコードに等しいtoString()操作が隠されています.
	data = data.toString() + chunk.toString();

ワイドバイトの中国語では、問題があります.
この問題をシミュレートするために、ファイルストリームからBufferを読み込み、Bufferの長さを11に制限します.
コードは次のとおりです.
var rs = fs.createReadStream('test.md', {
     highWaterMark: 11});

test.mdデータは李白の《静夜思》で、プログラムを実行して、以下の出力を得ることができます:ベッドの前は明るく???光;疑い???地上の霜首を挙げる??明月、???故郷に思いを馳せる.

6.3.1文字化けしはどのように発生したのか


詩には複数の疑問符が現れ、発生の原因を分析した.
Bufferの長さが11に制限されているため、ストリームから7回読み取ってからすべてのデータの読み取りが完了し、結果は以下の通りである.

前述のBufferのデフォルトの符号化はutf-8であり、中国語バイトutf-8の符号化では3バイトを占めているため、1回目のBuffer読み取りが完了すると3つの中国語が表示され、残りの2バイト(e 6 9 c)には乱符号(疑問符)が表示される.
2番目のBufferオブジェクトの1バイト目も文字化できず、文字化けしてしか表示できません.この例では、Bufferを11バイトに制限することで疑問符が発生し、Bufferバイトの長さを緩和すると、この問題は依然として発生します.

6.3.2 setEncoding()とstring_decoder()


ストリームを読み込むときに、ストリームの符号化方式を設定できます.サンプルコードは次のとおりです.
readable.setEncoding(encoding)

改善されたコードは次のとおりです.
var rs = fs.createReadStream('test.md', {
      highWaterMark: 11}); 
rs.setEncoding('utf8');

プログラムを再実行した後、正しい中国語を出力したことに驚きました.
setEncoding()を呼び出すと、読み取り可能なストリームオブジェクトにdecoderオブジェクトが内部に設定されます.このオブジェクトはstring_からdecoderモジュールStringDecoderインスタンスオブジェクト.
以下、コードで具体的に説明する.
	var StringDecoder = require('string_decoder').StringDecoder; 
	var decoder = new StringDecoder('utf8'); 

	var buf1 = new Buffer([0xE5, 0xBA, 0x8A, 0xE5, 0x89, 0x8D, 0xE6, 0x98, 0x8E, 0xE6, 0x9C]); 
	console.log(decoder.write(buf1)); 
	
	var buf2 = new Buffer([0x88, 0xE5, 0x85, 0x89, 0xEF, 0xBC, 0x8C, 0xE7, 0x96, 0x91, 0xE6]); 
	console.log(decoder.write(buf2));

以前の2つのBufferオブジェクトをdecoderに書き込むと、不思議なことに「月」のトランスコードが通常のように2つの部分で別々に出力されていないことがわかります.StringDecoderは、符号化された後、ワイドバイト文字列がutf-8符号化で3バイトで格納されていることを知り、最初のwrite()の場合、9バイトのトランスコードで形成された文字しか出力しない.2回目のwriteでは,残り2バイトと11バイトを一括して3バイトトランスコードする.

6.3.3正確な接合Buffer


文字列を正しくつなぎ合わせるにはどうすればいいですか?コードは次のとおりです.
	var chunks = []; 
	var size = 0; 
	
	res.on('data', function (chunk) {
      
 		chunks.push(chunk); 
 		size += chunk.length; 
	}); 
	
	res.on('end', function () {
     
		var buf = Buffer.concat(chunks, size); 
 		var str = iconv.decode(buf, 'utf8'); 
 		console.log(str); 
	});

正確な接合方法は、全てのBufferフラグメントを受信し、全てのフラグメントの全長を記録した後、Bufferを呼び出す配列である.concat()メソッドは、結合されたBufferオブジェクトを生成します.Buffer.concat()メソッドは、Bufferオブジェクトから大きなBufferオブジェクトへのレプリケーションプロセスをカプセル化します.
Buffer.concat = function(list, length) {
      
	if (!Array.isArray(list)) {
      
 		throw new Error('Usage: Buffer.concat(list, [length])'); 
 	} 
 
 	if (list.length === 0) {
      
 		return new Buffer(0); 
 	} else if (list.length === 1) {
      
 		return list[0]; 
 	} 
 
 	if (typeof length !== 'number') {
      
 		length = 0; 
	 	for (var i = 0; i < list.length; i++) {
      
 			var buf = list[i]; 
 			length += buf.length; 
 		} 
 	} 
 
 	var buffer = new Buffer(length); 
 	var pos = 0; 
 
 	for (var i = 0; i < list.length; i++) {
      
 		var buf = list[i]; 
 		buf.copy(buffer, pos); 
 		pos += buf.length; 
 	} 
 
 	return buffer; 
};